From 37846281554f75fa6473753cdd961b62c89bb9e3 Mon Sep 17 00:00:00 2001 From: Urs Fleisch Date: Sat, 23 Mar 2024 16:35:43 +0100 Subject: [PATCH] Provide equal operator for MP4::Item This is needed to generate MP4::ItemMap bindings with SWIG. --- taglib/mp4/mp4coverart.cpp | 10 ++++ taglib/mp4/mp4coverart.h | 11 ++++ taglib/mp4/mp4item.cpp | 55 +++++++++++++++++++ taglib/mp4/mp4item.h | 29 ++++++++++ tests/test_mp4item.cpp | 107 +++++++++++++++++++++++++++++++++++++ 5 files changed, 212 insertions(+) diff --git a/taglib/mp4/mp4coverart.cpp b/taglib/mp4/mp4coverart.cpp index 248ff3ac..f8f6d372 100644 --- a/taglib/mp4/mp4coverart.cpp +++ b/taglib/mp4/mp4coverart.cpp @@ -69,3 +69,13 @@ MP4::CoverArt::data() const { return d->data; } + +bool MP4::CoverArt::operator==(const CoverArt &other) const +{ + return format() == other.format() && data() == other.data(); +} + +bool MP4::CoverArt::operator!=(const CoverArt &other) const +{ + return !(*this == other); +} diff --git a/taglib/mp4/mp4coverart.h b/taglib/mp4/mp4coverart.h index e1030db0..780828ae 100644 --- a/taglib/mp4/mp4coverart.h +++ b/taglib/mp4/mp4coverart.h @@ -69,6 +69,17 @@ namespace TagLib { //! The image data ByteVector data() const; + /*! + * Returns \c true if the CoverArt and \a other are of the same format and + * contain the same data. + */ + bool operator==(const CoverArt &other) const; + + /*! + * Returns \c true if the CoverArt and \a other differ in format or data. + */ + bool operator!=(const CoverArt &other) const; + private: class CoverArtPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE diff --git a/taglib/mp4/mp4item.cpp b/taglib/mp4/mp4item.cpp index bce5d953..b1b14d03 100644 --- a/taglib/mp4/mp4item.cpp +++ b/taglib/mp4/mp4item.cpp @@ -30,6 +30,7 @@ using namespace TagLib; class MP4::Item::ItemPrivate { public: + Type type; bool valid { true }; AtomDataType atomDataType { TypeUndefined }; union { @@ -48,6 +49,7 @@ public: MP4::Item::Item() : d(std::make_shared()) { + d->type = Type::Void; d->valid = false; } @@ -67,36 +69,42 @@ MP4::Item::~Item() = default; MP4::Item::Item(bool value) : d(std::make_shared()) { + d->type = Type::Bool; d->m_bool = value; } MP4::Item::Item(int value) : d(std::make_shared()) { + d->type = Type::Int; d->m_int = value; } MP4::Item::Item(unsigned char value) : d(std::make_shared()) { + d->type = Type::Byte; d->m_byte = value; } MP4::Item::Item(unsigned int value) : d(std::make_shared()) { + d->type = Type::UInt; d->m_uint = value; } MP4::Item::Item(long long value) : d(std::make_shared()) { + d->type = Type::LongLong; d->m_longlong = value; } MP4::Item::Item(int value1, int value2) : d(std::make_shared()) { + d->type = Type::IntPair; d->m_intPair.first = value1; d->m_intPair.second = value2; } @@ -104,18 +112,21 @@ MP4::Item::Item(int value1, int value2) : MP4::Item::Item(const ByteVectorList &value) : d(std::make_shared()) { + d->type = Type::ByteVectorList; d->m_byteVectorList = value; } MP4::Item::Item(const StringList &value) : d(std::make_shared()) { + d->type = Type::StringList; d->m_stringList = value; } MP4::Item::Item(const MP4::CoverArtList &value) : d(std::make_shared()) { + d->type = Type::CoverArtList; d->m_coverArtList = value; } @@ -188,3 +199,47 @@ MP4::Item::isValid() const { return d->valid; } + +MP4::Item::Type MP4::Item::type() const +{ + return d->type; +} + +bool MP4::Item::operator==(const Item &other) const +{ + if(isValid() && other.isValid() && + type() == other.type() && + atomDataType() == other.atomDataType()) { + switch(type()) { + case Type::Void: + return true; + case Type::Bool: + return toBool() == other.toBool(); + case Type::Int: + return toInt() == other.toInt(); + case Type::IntPair: { + const auto lhs = toIntPair(); + const auto rhs = other.toIntPair(); + return lhs.first == rhs.first && lhs.second == rhs.second; + } + case Type::Byte: + return toByte() == other.toByte(); + case Type::UInt: + return toUInt() == other.toUInt(); + case Type::LongLong: + return toLongLong() == other.toLongLong(); + case Type::StringList: + return toStringList() == other.toStringList(); + case Type::ByteVectorList: + return toByteVectorList() == other.toByteVectorList(); + case Type::CoverArtList: + return toCoverArtList() == other.toCoverArtList(); + } + } + return false; +} + +bool MP4::Item::operator!=(const Item &other) const +{ + return !(*this == other); +} diff --git a/taglib/mp4/mp4item.h b/taglib/mp4/mp4item.h index 4638ea94..8f6ae9a8 100644 --- a/taglib/mp4/mp4item.h +++ b/taglib/mp4/mp4item.h @@ -36,6 +36,22 @@ namespace TagLib { class TAGLIB_EXPORT Item { public: + /*! + * The data type stored in the item. + */ + enum class Type : unsigned char { + Void, + Bool, + Int, + IntPair, + Byte, + UInt, + LongLong, + StringList, + ByteVectorList, + CoverArtList + }; + struct IntPair { int first, second; }; @@ -80,6 +96,19 @@ namespace TagLib { bool isValid() const; + Type type() const; + + /*! + * Returns \c true if the Item and \a other are of the same type and + * contain the same value. + */ + bool operator==(const Item &other) const; + + /*! + * Returns \c true if the Item and \a other differ in type or value. + */ + bool operator!=(const Item &other) const; + private: class ItemPrivate; TAGLIB_MSVC_SUPPRESS_WARNING_NEEDS_TO_HAVE_DLL_INTERFACE diff --git a/tests/test_mp4item.cpp b/tests/test_mp4item.cpp index d5a49ca3..8c322817 100644 --- a/tests/test_mp4item.cpp +++ b/tests/test_mp4item.cpp @@ -39,6 +39,7 @@ class TestMP4Item : public CppUnit::TestFixture { CPPUNIT_TEST_SUITE(TestMP4Item); CPPUNIT_TEST(testCoverArtList); + CPPUNIT_TEST(testItemOperations); CPPUNIT_TEST_SUITE_END(); public: @@ -58,6 +59,112 @@ public: CPPUNIT_ASSERT_EQUAL(ByteVector("bar"), l[1].data()); } + void testItemOperations() + { + MP4::Item e; + MP4::Item i1(1); + MP4::Item i2(1); + MP4::Item i3(-1); + MP4::Item c1(static_cast('A')); + MP4::Item c2(static_cast('A')); + MP4::Item c3(static_cast('Z')); + MP4::Item u1(2U); + MP4::Item u2(2U); + MP4::Item u3(0U); + MP4::Item l1(3LL); + MP4::Item l2(3LL); + MP4::Item l3(-7LL); + MP4::Item b1(true); + MP4::Item b2(true); + MP4::Item b3(false); + MP4::Item p1(4, 5); + MP4::Item p2(4, 5); + MP4::Item p3(-4, -5); + MP4::Item s1(StringList{"abc", "de"}); + MP4::Item s2(StringList{"abc", "de"}); + MP4::Item s3(StringList{"abc"}); + MP4::Item v1(ByteVectorList{"f", "gh"}); + MP4::Item v2(ByteVectorList{"f", "gh"}); + MP4::Item v3(ByteVectorList{}); + MP4::Item a1(MP4::CoverArtList{ + MP4::CoverArt(MP4::CoverArt::PNG, "foo"), + MP4::CoverArt(MP4::CoverArt::JPEG, "bar") + }); + MP4::Item a2(MP4::CoverArtList{ + MP4::CoverArt(MP4::CoverArt::PNG, "foo"), + MP4::CoverArt(MP4::CoverArt::JPEG, "bar") + }); + MP4::Item a3(MP4::CoverArtList{ + MP4::CoverArt(MP4::CoverArt::JPEG, "bar") + }); + + CPPUNIT_ASSERT(i1 == i2); + CPPUNIT_ASSERT(i2 != i3); + CPPUNIT_ASSERT(i3 != c1); + CPPUNIT_ASSERT(c1 == c1); + CPPUNIT_ASSERT(c1 == c2); + CPPUNIT_ASSERT(c2 != c3); + CPPUNIT_ASSERT(c3 != u1); + CPPUNIT_ASSERT(u1 == u2); + CPPUNIT_ASSERT(u2 != u3); + CPPUNIT_ASSERT(u3 != l1); + CPPUNIT_ASSERT(l1 == l2); + CPPUNIT_ASSERT(l2 != l3); + CPPUNIT_ASSERT(l3 != b1); + CPPUNIT_ASSERT(b1 == b2); + CPPUNIT_ASSERT(b2 != b3); + CPPUNIT_ASSERT(b3 != p1); + CPPUNIT_ASSERT(p1 == p2); + CPPUNIT_ASSERT(p2 != p3); + CPPUNIT_ASSERT(p3 != s1); + CPPUNIT_ASSERT(s1 == s2); + CPPUNIT_ASSERT(s2 != s3); + CPPUNIT_ASSERT(s3 != v1); + CPPUNIT_ASSERT(v1 == v2); + CPPUNIT_ASSERT(v2 != v3); + CPPUNIT_ASSERT(v3 != a1); + CPPUNIT_ASSERT(a1 == a2); + CPPUNIT_ASSERT(a2 != a3); + CPPUNIT_ASSERT(a3 != e); + + CPPUNIT_ASSERT(!e.isValid()); + CPPUNIT_ASSERT(i1.isValid()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Void, e.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Int, i1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Byte, c1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::UInt, u1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::LongLong, l1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::Bool, b1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::IntPair, p1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::StringList, s1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::ByteVectorList, v1.type()); + CPPUNIT_ASSERT_EQUAL(MP4::Item::Type::CoverArtList, a1.type()); + + CPPUNIT_ASSERT_EQUAL(1, i1.toInt()); + CPPUNIT_ASSERT_EQUAL(static_cast('A'), c1.toByte()); + CPPUNIT_ASSERT_EQUAL(2U, u1.toUInt()); + CPPUNIT_ASSERT_EQUAL(3LL, l1.toLongLong()); + CPPUNIT_ASSERT_EQUAL(true, b1.toBool()); + CPPUNIT_ASSERT_EQUAL(4, p1.toIntPair().first); + CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s1.toStringList()); + CPPUNIT_ASSERT_EQUAL((ByteVectorList{"f", "gh"}), v1.toByteVectorList()); + CPPUNIT_ASSERT_EQUAL(MP4::CoverArt::PNG, a1.toCoverArtList().front().format()); + + s3.swap(s1); + CPPUNIT_ASSERT_EQUAL((StringList{"abc"}), s1.toStringList()); + CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s3.toStringList()); + CPPUNIT_ASSERT_EQUAL(MP4::AtomDataType::TypeUndefined, s1.atomDataType()); + s1.setAtomDataType(MP4::AtomDataType::TypeUTF8); + CPPUNIT_ASSERT_EQUAL(MP4::AtomDataType::TypeUTF8, s1.atomDataType()); + s1 = s3; + CPPUNIT_ASSERT_EQUAL((StringList{"abc", "de"}), s1.toStringList()); + + MP4::ItemMap m1{{"key1", i1}, {"key2", p1}}; + MP4::ItemMap m2{{"key1", i2}, {"key2", p2}}; + MP4::ItemMap m3{{"key1", i2}, {"key2", p3}}; + CPPUNIT_ASSERT(m1 == m2); + CPPUNIT_ASSERT(m1 != m3); + } }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMP4Item);