From 8905e7095aa97dac4a047adca9e040f2e9ea1ffa Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 19 Dec 2016 10:37:10 +0900 Subject: [PATCH 01/37] Safer conversion of boolean values in ASF attributes. Technically, boolean values in Extended Content Description Object is not necessarily be 0 or 1. --- taglib/asf/asfattribute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taglib/asf/asfattribute.cpp b/taglib/asf/asfattribute.cpp index 1e6ed705..66c1996a 100644 --- a/taglib/asf/asfattribute.cpp +++ b/taglib/asf/asfattribute.cpp @@ -217,10 +217,10 @@ String ASF::Attribute::parse(ASF::File &f, int kind) case BoolType: if(kind == 0) { - d->boolValue = (readDWORD(&f) == 1); + d->boolValue = (readDWORD(&f) != 0); } else { - d->boolValue = (readWORD(&f) == 1); + d->boolValue = (readWORD(&f) != 0); } break; From ffa32b19a76e07ffce41adb77133dd43552751b1 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 20 Dec 2016 11:48:14 +0900 Subject: [PATCH 02/37] Fix the CMake check for std::atomic_int. std::atomic_int of Visual C++ 2012 cannot be constructed with integer. --- ConfigureChecks.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 4b55f273..98751b06 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -46,7 +46,7 @@ endif() check_cxx_source_compiles(" #include int main() { - std::atomic_int x; + std::atomic_int x(1); ++x; --x; return 0; From 14c3ce5737e17671045d70f4eff3bd0fca91ad99 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 20 Dec 2016 11:54:06 +0900 Subject: [PATCH 03/37] Remove all the optional dependencies on Boost. --- ConfigureChecks.cmake | 68 +++++++++++++++++------------------------ config.h.cmake | 1 - taglib/toolkit/tutils.h | 22 +++---------- 3 files changed, 32 insertions(+), 59 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index 98751b06..f69b4380 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -103,69 +103,57 @@ endif() # Determine which kind of byte swap functions your compiler supports. check_cxx_source_compiles(" - #include int main() { - boost::endian::endian_reverse(static_cast(1)); - boost::endian::endian_reverse(static_cast(1)); - boost::endian::endian_reverse(static_cast(1)); + __builtin_bswap16(0); + __builtin_bswap32(0); + __builtin_bswap64(0); return 0; } -" HAVE_BOOST_BYTESWAP) +" HAVE_GCC_BYTESWAP) -if(NOT HAVE_BOOST_BYTESWAP) +if(NOT HAVE_GCC_BYTESWAP) check_cxx_source_compiles(" + #include int main() { - __builtin_bswap16(0); - __builtin_bswap32(0); - __builtin_bswap64(0); + __bswap_16(0); + __bswap_32(0); + __bswap_64(0); return 0; } - " HAVE_GCC_BYTESWAP) + " HAVE_GLIBC_BYTESWAP) - if(NOT HAVE_GCC_BYTESWAP) + if(NOT HAVE_GLIBC_BYTESWAP) check_cxx_source_compiles(" - #include + #include int main() { - __bswap_16(0); - __bswap_32(0); - __bswap_64(0); + _byteswap_ushort(0); + _byteswap_ulong(0); + _byteswap_uint64(0); return 0; } - " HAVE_GLIBC_BYTESWAP) + " HAVE_MSC_BYTESWAP) - if(NOT HAVE_GLIBC_BYTESWAP) + if(NOT HAVE_MSC_BYTESWAP) check_cxx_source_compiles(" - #include + #include int main() { - _byteswap_ushort(0); - _byteswap_ulong(0); - _byteswap_uint64(0); + OSSwapInt16(0); + OSSwapInt32(0); + OSSwapInt64(0); return 0; } - " HAVE_MSC_BYTESWAP) + " HAVE_MAC_BYTESWAP) - if(NOT HAVE_MSC_BYTESWAP) + if(NOT HAVE_MAC_BYTESWAP) check_cxx_source_compiles(" - #include + #include int main() { - OSSwapInt16(0); - OSSwapInt32(0); - OSSwapInt64(0); + swap16(0); + swap32(0); + swap64(0); return 0; } - " HAVE_MAC_BYTESWAP) - - if(NOT HAVE_MAC_BYTESWAP) - check_cxx_source_compiles(" - #include - int main() { - swap16(0); - swap32(0); - swap64(0); - return 0; - } - " HAVE_OPENBSD_BYTESWAP) - endif() + " HAVE_OPENBSD_BYTESWAP) endif() endif() endif() diff --git a/config.h.cmake b/config.h.cmake index fd716134..c1a31094 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -4,7 +4,6 @@ #define TAGLIB_CONFIG_H /* Defined if your compiler supports some byte swap functions */ -#cmakedefine HAVE_BOOST_BYTESWAP 1 #cmakedefine HAVE_GCC_BYTESWAP 1 #cmakedefine HAVE_GLIBC_BYTESWAP 1 #cmakedefine HAVE_MSC_BYTESWAP 1 diff --git a/taglib/toolkit/tutils.h b/taglib/toolkit/tutils.h index b114619e..6d96cd12 100644 --- a/taglib/toolkit/tutils.h +++ b/taglib/toolkit/tutils.h @@ -34,9 +34,7 @@ # include #endif -#if defined(HAVE_BOOST_BYTESWAP) -# include -#elif defined(HAVE_MSC_BYTESWAP) +#if defined(HAVE_MSC_BYTESWAP) # include #elif defined(HAVE_GLIBC_BYTESWAP) # include @@ -63,11 +61,7 @@ namespace TagLib */ inline unsigned short byteSwap(unsigned short x) { -#if defined(HAVE_BOOST_BYTESWAP) - - return boost::endian::endian_reverse(static_cast(x)); - -#elif defined(HAVE_GCC_BYTESWAP) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap16(x); @@ -99,11 +93,7 @@ namespace TagLib */ inline unsigned int byteSwap(unsigned int x) { -#if defined(HAVE_BOOST_BYTESWAP) - - return boost::endian::endian_reverse(static_cast(x)); - -#elif defined(HAVE_GCC_BYTESWAP) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap32(x); @@ -138,11 +128,7 @@ namespace TagLib */ inline unsigned long long byteSwap(unsigned long long x) { -#if defined(HAVE_BOOST_BYTESWAP) - - return boost::endian::endian_reverse(static_cast(x)); - -#elif defined(HAVE_GCC_BYTESWAP) +#if defined(HAVE_GCC_BYTESWAP) return __builtin_bswap64(x); From de87cd7736d52302db330c830adae50b1e667954 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 20 Dec 2016 12:06:21 +0900 Subject: [PATCH 04/37] Remove the CMake check for Boost I missed out on. --- ConfigureChecks.cmake | 7 ------- 1 file changed, 7 deletions(-) diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index f69b4380..e39d97b2 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -34,13 +34,6 @@ if(NOT ${SIZEOF_DOUBLE} EQUAL 8) message(FATAL_ERROR "TagLib requires that double is 64-bit wide.") endif() -# Enable check_cxx_source_compiles() to work with Boost "header-only" libraries. - -find_package(Boost) -if(Boost_FOUND) - set(CMAKE_REQUIRED_INCLUDES "${CMAKE_REQUIRED_INCLUDES};${Boost_INCLUDE_DIRS}") -endif() - # Determine which kind of atomic operations your compiler supports. check_cxx_source_compiles(" From 83c72518ab2bf9559a0fbcdabcf27e9a10722215 Mon Sep 17 00:00:00 2001 From: Ramesh Shanmugasundaram Date: Thu, 22 Dec 2016 15:13:39 +0000 Subject: [PATCH 05/37] Renamed unsupported-extension.xxx and modified test The file name unsupported-extension.xxx causes issue when unpacked taglib-xxx.gz over an NFS partition. The file extension ".xxx" is the one NFS uses for its own purpose and hence it will not allow creation of this file. Hence renamed the file and modified the test cases that checks for this file. Signed-off-by: Ramesh Shanmugasundaram --- ...orted-extension.xxx => unsupported-extension.xx} | Bin tests/test_fileref.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename tests/data/{unsupported-extension.xxx => unsupported-extension.xx} (100%) diff --git a/tests/data/unsupported-extension.xxx b/tests/data/unsupported-extension.xx similarity index 100% rename from tests/data/unsupported-extension.xxx rename to tests/data/unsupported-extension.xx diff --git a/tests/test_fileref.cpp b/tests/test_fileref.cpp index 2d77682a..1b899975 100644 --- a/tests/test_fileref.cpp +++ b/tests/test_fileref.cpp @@ -233,7 +233,7 @@ public: FileRef f1(TEST_FILE_PATH_C("no-extension")); CPPUNIT_ASSERT(f1.isNull()); - FileRef f2(TEST_FILE_PATH_C("unsupported-extension.xxx")); + FileRef f2(TEST_FILE_PATH_C("unsupported-extension.xx")); CPPUNIT_ASSERT(f2.isNull()); } From c97be6630eaa317ca0f657a6311e0de3501d3f9a Mon Sep 17 00:00:00 2001 From: Michael Helmling Date: Fri, 6 Jan 2017 09:08:02 +0100 Subject: [PATCH 06/37] Fix #789 (typo in member doc) --- taglib/toolkit/tfile.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index a6dda7ba..55f53a52 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -86,7 +86,7 @@ namespace TagLib { * format, the returned map's unsupportedData() list will contain one entry identifying * that object (e.g. the frame type for ID3v2 tags). Use removeUnsupportedProperties() * to remove (a subset of) them. - * For files that contain more than one tag (e.g. an MP3 with both an ID3v2 and an ID3v2 + * For files that contain more than one tag (e.g. an MP3 with both an ID3v1 and an ID3v2 * tag) only the most "modern" one will be exported (ID3v2 in this case). * BIC: Will be made virtual in future releases. */ From 56a7656c2ea87a16587f0f124b28f43bf29ec8a7 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 8 Jan 2017 01:43:04 +0900 Subject: [PATCH 07/37] Remove some TODO comments which are no longer necessary. --- taglib/flac/flacfile.cpp | 1 - taglib/mpeg/id3v2/id3v2tag.cpp | 1 - 2 files changed, 2 deletions(-) diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index 93a0e952..b2014886 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -198,7 +198,6 @@ bool FLAC::File::save() } // Compute the amount of padding, and append that to data. - // TODO: Should be calculated in offset_t in taglib2. long originalLength = d->streamStart - d->flacStart; long paddingLength = originalLength - data.size() - 4; diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index 810b2aef..e8de7252 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -619,7 +619,6 @@ ByteVector ID3v2::Tag::render(int version) const } // Compute the amount of padding, and append that to tagData. - // TODO: Should be calculated in long long in taglib2. long originalSize = d->header.tagSize(); long paddingSize = originalSize - (tagData.size() - Header::size()); From 13be28a52c72e0b647cd1fd4d43688d4b85c21cb Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 8 Jan 2017 02:11:57 +0900 Subject: [PATCH 08/37] Be tolerant of empty FLAC seektable blocks. --- taglib/flac/flacfile.cpp | 4 +++- tests/data/empty-seektable.flac | Bin 0 -> 4608 bytes tests/test_flac.cpp | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/data/empty-seektable.flac diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index b2014886..6b224103 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -506,7 +506,9 @@ void FLAC::File::scan() return; } - if(blockLength == 0 && blockType != MetadataBlock::Padding) { + if(blockLength == 0 + && blockType != MetadataBlock::Padding && blockType != MetadataBlock::SeekTable) + { debug("FLAC::File::scan() -- Zero-sized metadata block found"); setValid(false); return; diff --git a/tests/data/empty-seektable.flac b/tests/data/empty-seektable.flac new file mode 100644 index 0000000000000000000000000000000000000000..20dd90d91665a122693ab1150771f7e11b2a136a GIT binary patch literal 4608 zcmYfENpxmlU{DfZ5CBpF3^snE9nA%ds%Am`S618!`pG=WiAO1a?vo!EnSt^w3=A3y zK)NV3Ewv~$FF92qCo{>-$I)5AP|r}$Si#7^z}&#n&>X0Qp%H8%h&2jELtr!nMnhmU z1V%$(Gz3ONU^E0qLtr!nMnhmU1V%$(Gz5lo2>kzXP=?_+%!jv&KzzmnF#ZZR5TEHJ zjBhIi;xnIv@%{8be3siV{`BP_KI5ukl7IUW>Rd}qpmU8NH$P&vXZ=4rS)$B?)zqqGKj5Ef2j`C%_OwDPNw3RL# zp0QBu*{U-;cb9a2)(WyRo*B5_yf;%^_;baz+}%aiS*JTbBu0C5DIU?7c;uJb+C)*M zh#t)uE0Q-wc`q^KQr(?5u}?*#Yu2HvGa7UEomj-By3q literal 0 HcmV?d00001 diff --git a/tests/test_flac.cpp b/tests/test_flac.cpp index f2065e09..5bae7d88 100644 --- a/tests/test_flac.cpp +++ b/tests/test_flac.cpp @@ -63,6 +63,7 @@ class TestFLAC : public CppUnit::TestFixture CPPUNIT_TEST(testEmptyID3v2); CPPUNIT_TEST(testStripTags); CPPUNIT_TEST(testRemoveXiphField); + CPPUNIT_TEST(testEmptySeekTable); CPPUNIT_TEST_SUITE_END(); public: @@ -516,6 +517,24 @@ public: } } + void testEmptySeekTable() + { + ScopedFileCopy copy("empty-seektable", ".flac"); + { + FLAC::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + f.xiphComment(true)->setTitle("XiphComment Title"); + f.save(); + } + { + FLAC::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + f.seek(42); + const ByteVector data = f.readBlock(4); + CPPUNIT_ASSERT_EQUAL(ByteVector("\x03\x00\x00\x00", 4), data); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestFLAC); From 193cbe3b6b019fd83fbc86dcd73591cb68aec2c9 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 14 Jan 2017 23:29:46 +0900 Subject: [PATCH 09/37] Initialize all the data members of ASF::Attribute. --- taglib/asf/asfattribute.cpp | 52 +++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 28 deletions(-) diff --git a/taglib/asf/asfattribute.cpp b/taglib/asf/asfattribute.cpp index 66c1996a..ec909fe8 100644 --- a/taglib/asf/asfattribute.cpp +++ b/taglib/asf/asfattribute.cpp @@ -36,20 +36,16 @@ using namespace TagLib; class ASF::Attribute::AttributePrivate : public RefCounter { public: - AttributePrivate() - : pictureValue(ASF::Picture::fromInvalid()), - stream(0), - language(0) {} + AttributePrivate() : + pictureValue(ASF::Picture::fromInvalid()), + numericValue(0), + stream(0), + language(0) {} AttributeTypes type; String stringValue; ByteVector byteVectorValue; ASF::Picture pictureValue; - union { - unsigned int intValue; - unsigned short shortValue; - unsigned long long longLongValue; - bool boolValue; - }; + unsigned long long numericValue; int stream; int language; }; @@ -95,28 +91,28 @@ ASF::Attribute::Attribute(unsigned int value) : d(new AttributePrivate()) { d->type = DWordType; - d->intValue = value; + d->numericValue = value; } ASF::Attribute::Attribute(unsigned long long value) : d(new AttributePrivate()) { d->type = QWordType; - d->longLongValue = value; + d->numericValue = value; } ASF::Attribute::Attribute(unsigned short value) : d(new AttributePrivate()) { d->type = WordType; - d->shortValue = value; + d->numericValue = value; } ASF::Attribute::Attribute(bool value) : d(new AttributePrivate()) { d->type = BoolType; - d->boolValue = value; + d->numericValue = value; } ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other) @@ -157,22 +153,22 @@ ByteVector ASF::Attribute::toByteVector() const unsigned short ASF::Attribute::toBool() const { - return d->shortValue; + return d->numericValue ? 1 : 0; } unsigned short ASF::Attribute::toUShort() const { - return d->shortValue; + return static_cast(d->numericValue); } unsigned int ASF::Attribute::toUInt() const { - return d->intValue; + return static_cast(d->numericValue); } unsigned long long ASF::Attribute::toULongLong() const { - return d->longLongValue; + return static_cast(d->numericValue); } ASF::Picture ASF::Attribute::toPicture() const @@ -212,24 +208,24 @@ String ASF::Attribute::parse(ASF::File &f, int kind) switch(d->type) { case WordType: - d->shortValue = readWORD(&f); + d->numericValue = readWORD(&f); break; case BoolType: if(kind == 0) { - d->boolValue = (readDWORD(&f) != 0); + d->numericValue = (readDWORD(&f) != 0); } else { - d->boolValue = (readWORD(&f) != 0); + d->numericValue = (readWORD(&f) != 0); } break; case DWordType: - d->intValue = readDWORD(&f); + d->numericValue = readDWORD(&f); break; case QWordType: - d->longLongValue = readQWORD(&f); + d->numericValue = readQWORD(&f); break; case UnicodeType: @@ -280,24 +276,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const switch (d->type) { case WordType: - data.append(ByteVector::fromShort(d->shortValue, false)); + data.append(ByteVector::fromShort(toUShort(), false)); break; case BoolType: if(kind == 0) { - data.append(ByteVector::fromUInt(d->boolValue ? 1 : 0, false)); + data.append(ByteVector::fromUInt(toBool())); } else { - data.append(ByteVector::fromShort(d->boolValue ? 1 : 0, false)); + data.append(ByteVector::fromShort(toBool())); } break; case DWordType: - data.append(ByteVector::fromUInt(d->intValue, false)); + data.append(ByteVector::fromUInt(toUInt(), false)); break; case QWordType: - data.append(ByteVector::fromLongLong(d->longLongValue, false)); + data.append(ByteVector::fromLongLong(toULongLong(), false)); break; case UnicodeType: From 406e872ac3f6a59f6bdfe6cf9d2d5968aa3a9f39 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 16 Jan 2017 00:59:12 +0900 Subject: [PATCH 10/37] Always use parentheses with new. It's a bit safer. --- taglib/asf/asftag.cpp | 6 +-- taglib/flac/flacfile.cpp | 2 +- taglib/flac/flacpicture.cpp | 8 ++-- taglib/flac/flacunknownmetadatablock.cpp | 5 +-- taglib/it/itproperties.cpp | 2 +- taglib/mod/modproperties.cpp | 2 +- taglib/mod/modtag.cpp | 5 ++- .../id3v2/frames/attachedpictureframe.cpp | 15 +++++--- taglib/mpeg/id3v2/frames/chapterframe.cpp | 13 +++---- taglib/mpeg/id3v2/frames/commentsframe.cpp | 10 +++-- .../id3v2/frames/eventtimingcodesframe.cpp | 8 ++-- .../frames/generalencapsulatedobjectframe.cpp | 15 +++++--- taglib/mpeg/id3v2/frames/ownershipframe.cpp | 15 +++++--- taglib/mpeg/id3v2/frames/podcastframe.cpp | 10 +++-- .../mpeg/id3v2/frames/popularimeterframe.cpp | 15 +++++--- taglib/mpeg/id3v2/frames/privateframe.cpp | 10 +++-- .../mpeg/id3v2/frames/relativevolumeframe.cpp | 15 +++++--- .../id3v2/frames/synchronizedlyricsframe.cpp | 8 ++-- .../id3v2/frames/tableofcontentsframe.cpp | 12 +++--- .../id3v2/frames/textidentificationframe.cpp | 13 ++++--- .../frames/uniquefileidentifierframe.cpp | 12 +++--- taglib/mpeg/id3v2/frames/unknownframe.cpp | 10 +++-- .../frames/unsynchronizedlyricsframe.cpp | 8 ++-- taglib/mpeg/id3v2/frames/urllinkframe.cpp | 37 ++++++++++++++----- taglib/mpeg/id3v2/id3v2frame.cpp | 16 ++++---- taglib/mpeg/id3v2/id3v2tag.cpp | 2 +- taglib/ogg/flac/oggflacfile.cpp | 12 +++--- taglib/ogg/opus/opusfile.cpp | 2 +- taglib/ogg/speex/speexfile.cpp | 2 +- taglib/ogg/vorbis/vorbisfile.cpp | 2 +- taglib/s3m/s3mproperties.cpp | 2 +- taglib/tagunion.cpp | 5 +-- taglib/xm/xmproperties.cpp | 2 +- 33 files changed, 171 insertions(+), 130 deletions(-) diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp index ed2dba52..20a946f0 100644 --- a/taglib/asf/asftag.cpp +++ b/taglib/asf/asftag.cpp @@ -39,10 +39,10 @@ public: AttributeListMap attributeListMap; }; -ASF::Tag::Tag() -: TagLib::Tag() +ASF::Tag::Tag() : + TagLib::Tag(), + d(new TagPrivate()) { - d = new TagPrivate; } ASF::Tag::~Tag() diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index b2014886..09e184f0 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -300,7 +300,7 @@ ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) if(!create || d->tag[FlacID3v2Index]) return static_cast(d->tag[FlacID3v2Index]); - d->tag.set(FlacID3v2Index, new ID3v2::Tag); + d->tag.set(FlacID3v2Index, new ID3v2::Tag()); return static_cast(d->tag[FlacID3v2Index]); } diff --git a/taglib/flac/flacpicture.cpp b/taglib/flac/flacpicture.cpp index 72c97247..ec07ad14 100644 --- a/taglib/flac/flacpicture.cpp +++ b/taglib/flac/flacpicture.cpp @@ -50,14 +50,14 @@ public: ByteVector data; }; -FLAC::Picture::Picture() +FLAC::Picture::Picture() : + d(new PicturePrivate()) { - d = new PicturePrivate; } -FLAC::Picture::Picture(const ByteVector &data) +FLAC::Picture::Picture(const ByteVector &data) : + d(new PicturePrivate()) { - d = new PicturePrivate; parse(data); } diff --git a/taglib/flac/flacunknownmetadatablock.cpp b/taglib/flac/flacunknownmetadatablock.cpp index dcd5d651..f9cf6e65 100644 --- a/taglib/flac/flacunknownmetadatablock.cpp +++ b/taglib/flac/flacunknownmetadatablock.cpp @@ -39,11 +39,10 @@ public: ByteVector data; }; -FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) +FLAC::UnknownMetadataBlock::UnknownMetadataBlock(int code, const ByteVector &data) : + d(new UnknownMetadataBlockPrivate()) { - d = new UnknownMetadataBlockPrivate; d->code = code; - //debug(String(data.toHex())); d->data = data; } diff --git a/taglib/it/itproperties.cpp b/taglib/it/itproperties.cpp index 8f686dc7..c317b660 100644 --- a/taglib/it/itproperties.cpp +++ b/taglib/it/itproperties.cpp @@ -70,7 +70,7 @@ public: IT::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), - d(new PropertiesPrivate) + d(new PropertiesPrivate()) { } diff --git a/taglib/mod/modproperties.cpp b/taglib/mod/modproperties.cpp index ed3df94d..c6bf3947 100644 --- a/taglib/mod/modproperties.cpp +++ b/taglib/mod/modproperties.cpp @@ -46,7 +46,7 @@ public: Mod::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), - d(new PropertiesPrivate) + d(new PropertiesPrivate()) { } diff --git a/taglib/mod/modtag.cpp b/taglib/mod/modtag.cpp index 616d8c1b..0e8d0371 100644 --- a/taglib/mod/modtag.cpp +++ b/taglib/mod/modtag.cpp @@ -43,9 +43,10 @@ public: String trackerName; }; -Mod::Tag::Tag() : TagLib::Tag() +Mod::Tag::Tag() : + TagLib::Tag(), + d(new TagPrivate()) { - d = new TagPrivate; } Mod::Tag::~Tag() diff --git a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp index 849ee9ff..8e2630ce 100644 --- a/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp +++ b/taglib/mpeg/id3v2/frames/attachedpictureframe.cpp @@ -48,14 +48,16 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -AttachedPictureFrame::AttachedPictureFrame() : Frame("APIC") +AttachedPictureFrame::AttachedPictureFrame() : + Frame("APIC"), + d(new AttachedPictureFramePrivate()) { - d = new AttachedPictureFramePrivate; } -AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : Frame(data) +AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data) : + Frame(data), + d(new AttachedPictureFramePrivate()) { - d = new AttachedPictureFramePrivate; setData(data); } @@ -169,9 +171,10 @@ ByteVector AttachedPictureFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : Frame(h) +AttachedPictureFrame::AttachedPictureFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new AttachedPictureFramePrivate()) { - d = new AttachedPictureFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/chapterframe.cpp b/taglib/mpeg/id3v2/frames/chapterframe.cpp index e5c92b0e..69fe1df0 100644 --- a/taglib/mpeg/id3v2/frames/chapterframe.cpp +++ b/taglib/mpeg/id3v2/frames/chapterframe.cpp @@ -61,9 +61,9 @@ public: //////////////////////////////////////////////////////////////////////////////// ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : - ID3v2::Frame(data) + ID3v2::Frame(data), + d(new ChapterFramePrivate()) { - d = new ChapterFramePrivate; d->tagHeader = tagHeader; setData(data); } @@ -72,10 +72,9 @@ ChapterFrame::ChapterFrame(const ByteVector &elementID, unsigned int startTime, unsigned int endTime, unsigned int startOffset, unsigned int endOffset, const FrameList &embeddedFrames) : - ID3v2::Frame("CHAP") + ID3v2::Frame("CHAP"), + d(new ChapterFramePrivate()) { - d = new ChapterFramePrivate; - // setElementID has a workaround for a previously silly API where you had to // specifically include the null byte. @@ -302,9 +301,9 @@ ByteVector ChapterFrame::renderFields() const } ChapterFrame::ChapterFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : - Frame(h) + Frame(h), + d(new ChapterFramePrivate()) { - d = new ChapterFramePrivate; d->tagHeader = tagHeader; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp index b5618322..7dda83dc 100644 --- a/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -48,15 +48,17 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -CommentsFrame::CommentsFrame(String::Type encoding) : Frame("COMM") +CommentsFrame::CommentsFrame(String::Type encoding) : + Frame("COMM"), + d(new CommentsFramePrivate()) { - d = new CommentsFramePrivate; d->textEncoding = encoding; } -CommentsFrame::CommentsFrame(const ByteVector &data) : Frame(data) +CommentsFrame::CommentsFrame(const ByteVector &data) : + Frame(data), + d(new CommentsFramePrivate()) { - d = new CommentsFramePrivate; setData(data); } diff --git a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp index 7af797f0..d543ab31 100644 --- a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp +++ b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp @@ -46,15 +46,15 @@ public: //////////////////////////////////////////////////////////////////////////////// EventTimingCodesFrame::EventTimingCodesFrame() : - Frame("ETCO") + Frame("ETCO"), + d(new EventTimingCodesFramePrivate()) { - d = new EventTimingCodesFramePrivate; } EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data) : - Frame(data) + Frame(data), + d(new EventTimingCodesFramePrivate()) { - d = new EventTimingCodesFramePrivate; setData(data); } diff --git a/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp b/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp index 1c773837..c9b2f6dc 100644 --- a/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp +++ b/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp @@ -50,14 +50,16 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : Frame("GEOB") +GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame() : + Frame("GEOB"), + d(new GeneralEncapsulatedObjectFramePrivate()) { - d = new GeneralEncapsulatedObjectFramePrivate; } -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : Frame(data) +GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data) : + Frame(data), + d(new GeneralEncapsulatedObjectFramePrivate()) { - d = new GeneralEncapsulatedObjectFramePrivate; setData(data); } @@ -177,8 +179,9 @@ ByteVector GeneralEncapsulatedObjectFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : Frame(h) +GeneralEncapsulatedObjectFrame::GeneralEncapsulatedObjectFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new GeneralEncapsulatedObjectFramePrivate()) { - d = new GeneralEncapsulatedObjectFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/ownershipframe.cpp b/taglib/mpeg/id3v2/frames/ownershipframe.cpp index 83a59824..0b180dbf 100644 --- a/taglib/mpeg/id3v2/frames/ownershipframe.cpp +++ b/taglib/mpeg/id3v2/frames/ownershipframe.cpp @@ -45,15 +45,17 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -OwnershipFrame::OwnershipFrame(String::Type encoding) : Frame("OWNE") +OwnershipFrame::OwnershipFrame(String::Type encoding) : + Frame("OWNE"), + d(new OwnershipFramePrivate()) { - d = new OwnershipFramePrivate; d->textEncoding = encoding; } -OwnershipFrame::OwnershipFrame(const ByteVector &data) : Frame(data) +OwnershipFrame::OwnershipFrame(const ByteVector &data) : + Frame(data), + d(new OwnershipFramePrivate()) { - d = new OwnershipFramePrivate; setData(data); } @@ -161,8 +163,9 @@ ByteVector OwnershipFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : Frame(h) +OwnershipFrame::OwnershipFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new OwnershipFramePrivate()) { - d = new OwnershipFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/podcastframe.cpp b/taglib/mpeg/id3v2/frames/podcastframe.cpp index 5115a7dd..7285b968 100644 --- a/taglib/mpeg/id3v2/frames/podcastframe.cpp +++ b/taglib/mpeg/id3v2/frames/podcastframe.cpp @@ -38,9 +38,10 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -PodcastFrame::PodcastFrame() : Frame("PCST") +PodcastFrame::PodcastFrame() : + Frame("PCST"), + d(new PodcastFramePrivate()) { - d = new PodcastFramePrivate; d->fieldData = ByteVector(4, '\0'); } @@ -72,8 +73,9 @@ ByteVector PodcastFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : Frame(h) +PodcastFrame::PodcastFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new PodcastFramePrivate()) { - d = new PodcastFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/popularimeterframe.cpp b/taglib/mpeg/id3v2/frames/popularimeterframe.cpp index dac8589f..9106fabd 100644 --- a/taglib/mpeg/id3v2/frames/popularimeterframe.cpp +++ b/taglib/mpeg/id3v2/frames/popularimeterframe.cpp @@ -43,14 +43,16 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -PopularimeterFrame::PopularimeterFrame() : Frame("POPM") +PopularimeterFrame::PopularimeterFrame() : + Frame("POPM"), + d(new PopularimeterFramePrivate()) { - d = new PopularimeterFramePrivate; } -PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : Frame(data) +PopularimeterFrame::PopularimeterFrame(const ByteVector &data) : + Frame(data), + d(new PopularimeterFramePrivate()) { - d = new PopularimeterFramePrivate; setData(data); } @@ -130,8 +132,9 @@ ByteVector PopularimeterFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : Frame(h) +PopularimeterFrame::PopularimeterFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new PopularimeterFramePrivate()) { - d = new PopularimeterFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/privateframe.cpp b/taglib/mpeg/id3v2/frames/privateframe.cpp index 24ee0f35..8c0a29ff 100644 --- a/taglib/mpeg/id3v2/frames/privateframe.cpp +++ b/taglib/mpeg/id3v2/frames/privateframe.cpp @@ -45,14 +45,16 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -PrivateFrame::PrivateFrame() : Frame("PRIV") +PrivateFrame::PrivateFrame() : + Frame("PRIV"), + d(new PrivateFramePrivate()) { - d = new PrivateFramePrivate; } -PrivateFrame::PrivateFrame(const ByteVector &data) : Frame(data) +PrivateFrame::PrivateFrame(const ByteVector &data) : + Frame(data), + d(new PrivateFramePrivate()) { - d = new PrivateFramePrivate; setData(data); } diff --git a/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp index a907f6b9..3398ada8 100644 --- a/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp @@ -51,14 +51,16 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -RelativeVolumeFrame::RelativeVolumeFrame() : Frame("RVA2") +RelativeVolumeFrame::RelativeVolumeFrame() : + Frame("RVA2"), + d(new RelativeVolumeFramePrivate()) { - d = new RelativeVolumeFramePrivate; } -RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : Frame(data) +RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data) : + Frame(data), + d(new RelativeVolumeFramePrivate()) { - d = new RelativeVolumeFramePrivate; setData(data); } @@ -223,8 +225,9 @@ ByteVector RelativeVolumeFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : Frame(h) +RelativeVolumeFrame::RelativeVolumeFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new RelativeVolumeFramePrivate()) { - d = new RelativeVolumeFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp index c3b50c7e..e1559a72 100644 --- a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp @@ -52,16 +52,16 @@ public: //////////////////////////////////////////////////////////////////////////////// SynchronizedLyricsFrame::SynchronizedLyricsFrame(String::Type encoding) : - Frame("SYLT") + Frame("SYLT"), + d(new SynchronizedLyricsFramePrivate()) { - d = new SynchronizedLyricsFramePrivate; d->textEncoding = encoding; } SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data) : - Frame(data) + Frame(data), + d(new SynchronizedLyricsFramePrivate()) { - d = new SynchronizedLyricsFramePrivate; setData(data); } diff --git a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index b554d2cd..ddd3b88c 100644 --- a/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp @@ -82,9 +82,9 @@ namespace { //////////////////////////////////////////////////////////////////////////////// TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data) : - ID3v2::Frame(data) + ID3v2::Frame(data), + d(new TableOfContentsFramePrivate()) { - d = new TableOfContentsFramePrivate; d->tagHeader = tagHeader; setData(data); } @@ -92,9 +92,9 @@ TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const TableOfContentsFrame::TableOfContentsFrame(const ByteVector &elementID, const ByteVectorList &children, const FrameList &embeddedFrames) : - ID3v2::Frame("CTOC") + ID3v2::Frame("CTOC"), + d(new TableOfContentsFramePrivate()) { - d = new TableOfContentsFramePrivate; d->elementID = elementID; strip(d->elementID); d->childElements = children; @@ -332,9 +332,9 @@ ByteVector TableOfContentsFrame::renderFields() const TableOfContentsFrame::TableOfContentsFrame(const ID3v2::Header *tagHeader, const ByteVector &data, Header *h) : - Frame(h) + Frame(h), + d(new TableOfContentsFramePrivate()) { - d = new TableOfContentsFramePrivate; d->tagHeader = tagHeader; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp index d9d3b29b..db9a177e 100644 --- a/taglib/mpeg/id3v2/frames/textidentificationframe.cpp +++ b/taglib/mpeg/id3v2/frames/textidentificationframe.cpp @@ -45,16 +45,16 @@ public: //////////////////////////////////////////////////////////////////////////////// TextIdentificationFrame::TextIdentificationFrame(const ByteVector &type, String::Type encoding) : - Frame(type) + Frame(type), + d(new TextIdentificationFramePrivate()) { - d = new TextIdentificationFramePrivate; d->textEncoding = encoding; } TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data) : - Frame(data) + Frame(data), + d(new TextIdentificationFramePrivate()) { - d = new TextIdentificationFramePrivate; setData(data); } @@ -252,9 +252,10 @@ ByteVector TextIdentificationFrame::renderFields() const // TextIdentificationFrame private members //////////////////////////////////////////////////////////////////////////////// -TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : Frame(h) +TextIdentificationFrame::TextIdentificationFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new TextIdentificationFramePrivate()) { - d = new TextIdentificationFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp index a986e4db..fcb855b2 100644 --- a/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp +++ b/taglib/mpeg/id3v2/frames/uniquefileidentifierframe.cpp @@ -45,16 +45,16 @@ public: //////////////////////////////////////////////////////////////////////////////// UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data) : - ID3v2::Frame(data) + ID3v2::Frame(data), + d(new UniqueFileIdentifierFramePrivate()) { - d = new UniqueFileIdentifierFramePrivate; setData(data); } UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const String &owner, const ByteVector &id) : - ID3v2::Frame("UFID") + ID3v2::Frame("UFID"), + d(new UniqueFileIdentifierFramePrivate()) { - d = new UniqueFileIdentifierFramePrivate; d->owner = owner; d->identifier = id; } @@ -141,8 +141,8 @@ ByteVector UniqueFileIdentifierFrame::renderFields() const } UniqueFileIdentifierFrame::UniqueFileIdentifierFrame(const ByteVector &data, Header *h) : - Frame(h) + Frame(h), + d(new UniqueFileIdentifierFramePrivate()) { - d = new UniqueFileIdentifierFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/unknownframe.cpp b/taglib/mpeg/id3v2/frames/unknownframe.cpp index 9d059193..edb30084 100644 --- a/taglib/mpeg/id3v2/frames/unknownframe.cpp +++ b/taglib/mpeg/id3v2/frames/unknownframe.cpp @@ -38,9 +38,10 @@ public: // public members //////////////////////////////////////////////////////////////////////////////// -UnknownFrame::UnknownFrame(const ByteVector &data) : Frame(data) +UnknownFrame::UnknownFrame(const ByteVector &data) : + Frame(data), + d(new UnknownFramePrivate()) { - d = new UnknownFramePrivate; setData(data); } @@ -77,8 +78,9 @@ ByteVector UnknownFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : Frame(h) +UnknownFrame::UnknownFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new UnknownFramePrivate()) { - d = new UnknownFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 3d610c9a..2e6c4bae 100644 --- a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -50,16 +50,16 @@ public: //////////////////////////////////////////////////////////////////////////////// UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(String::Type encoding) : - Frame("USLT") + Frame("USLT"), + d(new UnsynchronizedLyricsFramePrivate()) { - d = new UnsynchronizedLyricsFramePrivate; d->textEncoding = encoding; } UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data) : - Frame(data) + Frame(data), + d(new UnsynchronizedLyricsFramePrivate()) { - d = new UnsynchronizedLyricsFramePrivate; setData(data); } diff --git a/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 42d80712..99e4fb7f 100644 --- a/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -49,10 +49,14 @@ public: String description; }; +//////////////////////////////////////////////////////////////////////////////// +// UrlLinkFrame public members +//////////////////////////////////////////////////////////////////////////////// + UrlLinkFrame::UrlLinkFrame(const ByteVector &data) : - Frame(data) + Frame(data), + d(new UrlLinkFramePrivate()) { - d = new UrlLinkFramePrivate; setData(data); } @@ -93,6 +97,10 @@ PropertyMap UrlLinkFrame::asProperties() const return map; } +//////////////////////////////////////////////////////////////////////////////// +// UrlLinkFrame protected members +//////////////////////////////////////////////////////////////////////////////// + void UrlLinkFrame::parseFields(const ByteVector &data) { d->url = String(data); @@ -103,24 +111,28 @@ ByteVector UrlLinkFrame::renderFields() const return d->url.data(String::Latin1); } -UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : Frame(h) +UrlLinkFrame::UrlLinkFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new UrlLinkFramePrivate()) { - d = new UrlLinkFramePrivate; parseFields(fieldData(data)); } +//////////////////////////////////////////////////////////////////////////////// +// UserUrlLinkFrame public members +//////////////////////////////////////////////////////////////////////////////// UserUrlLinkFrame::UserUrlLinkFrame(String::Type encoding) : - UrlLinkFrame("WXXX") + UrlLinkFrame("WXXX"), + d(new UserUrlLinkFramePrivate()) { - d = new UserUrlLinkFramePrivate; d->textEncoding = encoding; } UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data) : - UrlLinkFrame(data) + UrlLinkFrame(data), + d(new UserUrlLinkFramePrivate()) { - d = new UserUrlLinkFramePrivate; setData(data); } @@ -176,6 +188,10 @@ UserUrlLinkFrame *UserUrlLinkFrame::find(ID3v2::Tag *tag, const String &descript return 0; } +//////////////////////////////////////////////////////////////////////////////// +// UserUrlLinkFrame protected members +//////////////////////////////////////////////////////////////////////////////// + void UserUrlLinkFrame::parseFields(const ByteVector &data) { if(data.size() < 2) { @@ -222,8 +238,9 @@ ByteVector UserUrlLinkFrame::renderFields() const return v; } -UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : UrlLinkFrame(data, h) +UserUrlLinkFrame::UserUrlLinkFrame(const ByteVector &data, Header *h) : + UrlLinkFrame(data, h), + d(new UserUrlLinkFramePrivate()) { - d = new UserUrlLinkFramePrivate; parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index ec3b9310..4f88dec1 100644 --- a/taglib/mpeg/id3v2/id3v2frame.cpp +++ b/taglib/mpeg/id3v2/id3v2frame.cpp @@ -198,15 +198,15 @@ ByteVector Frame::render() const // protected members //////////////////////////////////////////////////////////////////////////////// -Frame::Frame(const ByteVector &data) +Frame::Frame(const ByteVector &data) : + d(new FramePrivate()) { - d = new FramePrivate; d->header = new Header(data); } -Frame::Frame(Header *h) +Frame::Frame(Header *h) : + d(new FramePrivate()) { - d = new FramePrivate; d->header = h; } @@ -573,15 +573,15 @@ unsigned int Frame::Header::size(unsigned int version) // public members (Frame::Header) //////////////////////////////////////////////////////////////////////////////// -Frame::Header::Header(const ByteVector &data, bool synchSafeInts) +Frame::Header::Header(const ByteVector &data, bool synchSafeInts) : + d(new HeaderPrivate()) { - d = new HeaderPrivate; setData(data, synchSafeInts); } -Frame::Header::Header(const ByteVector &data, unsigned int version) +Frame::Header::Header(const ByteVector &data, unsigned int version) : + d(new HeaderPrivate()) { - d = new HeaderPrivate; setData(data, version); } diff --git a/taglib/mpeg/id3v2/id3v2tag.cpp b/taglib/mpeg/id3v2/id3v2tag.cpp index e8de7252..525e88b6 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -723,7 +723,7 @@ void ID3v2::Tag::parse(const ByteVector &origData) if(d->header.extendedHeader()) { if(!d->extendedHeader) - d->extendedHeader = new ExtendedHeader; + d->extendedHeader = new ExtendedHeader(); d->extendedHeader->setData(data); if(d->extendedHeader->size() <= data.size()) { frameDataPosition += d->extendedHeader->size(); diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index 3a36ebe8..fe4d8830 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -70,17 +70,19 @@ public: //////////////////////////////////////////////////////////////////////////////// Ogg::FLAC::File::File(FileName file, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(file) + Properties::ReadStyle propertiesStyle) : + Ogg::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) read(readProperties, propertiesStyle); } Ogg::FLAC::File::File(IOStream *stream, bool readProperties, - Properties::ReadStyle propertiesStyle) : Ogg::File(stream) + Properties::ReadStyle propertiesStyle) : + Ogg::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) read(readProperties, propertiesStyle); } @@ -172,7 +174,7 @@ void Ogg::FLAC::File::read(bool readProperties, Properties::ReadStyle properties if(d->hasXiphComment) d->comment = new Ogg::XiphComment(xiphCommentData()); else - d->comment = new Ogg::XiphComment; + d->comment = new Ogg::XiphComment(); if(readProperties) diff --git a/taglib/ogg/opus/opusfile.cpp b/taglib/ogg/opus/opusfile.cpp index 43c102d0..ff1bfe2d 100644 --- a/taglib/ogg/opus/opusfile.cpp +++ b/taglib/ogg/opus/opusfile.cpp @@ -101,7 +101,7 @@ Opus::Properties *Opus::File::audioProperties() const bool Opus::File::save() { if(!d->comment) - d->comment = new Ogg::XiphComment; + d->comment = new Ogg::XiphComment(); setPacket(1, ByteVector("OpusTags", 8) + d->comment->render(false)); diff --git a/taglib/ogg/speex/speexfile.cpp b/taglib/ogg/speex/speexfile.cpp index 2d374aed..7af71d50 100644 --- a/taglib/ogg/speex/speexfile.cpp +++ b/taglib/ogg/speex/speexfile.cpp @@ -101,7 +101,7 @@ Speex::Properties *Speex::File::audioProperties() const bool Speex::File::save() { if(!d->comment) - d->comment = new Ogg::XiphComment; + d->comment = new Ogg::XiphComment(); setPacket(1, d->comment->render()); diff --git a/taglib/ogg/vorbis/vorbisfile.cpp b/taglib/ogg/vorbis/vorbisfile.cpp index 1e19e862..2773bd3b 100644 --- a/taglib/ogg/vorbis/vorbisfile.cpp +++ b/taglib/ogg/vorbis/vorbisfile.cpp @@ -109,7 +109,7 @@ bool Vorbis::File::save() ByteVector v(vorbisCommentHeaderID); if(!d->comment) - d->comment = new Ogg::XiphComment; + d->comment = new Ogg::XiphComment(); v.append(d->comment->render()); setPacket(1, v); diff --git a/taglib/s3m/s3mproperties.cpp b/taglib/s3m/s3mproperties.cpp index 69654b1a..e3e443cb 100644 --- a/taglib/s3m/s3mproperties.cpp +++ b/taglib/s3m/s3mproperties.cpp @@ -64,7 +64,7 @@ public: S3M::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), - d(new PropertiesPrivate) + d(new PropertiesPrivate()) { } diff --git a/taglib/tagunion.cpp b/taglib/tagunion.cpp index c10d72b2..64d786b9 100644 --- a/taglib/tagunion.cpp +++ b/taglib/tagunion.cpp @@ -79,10 +79,9 @@ public: std::vector tags; }; -TagUnion::TagUnion(Tag *first, Tag *second, Tag *third) +TagUnion::TagUnion(Tag *first, Tag *second, Tag *third) : + d(new TagUnionPrivate()) { - d = new TagUnionPrivate; - d->tags[0] = first; d->tags[1] = second; d->tags[2] = third; diff --git a/taglib/xm/xmproperties.cpp b/taglib/xm/xmproperties.cpp index 39a9fa0a..93d84986 100644 --- a/taglib/xm/xmproperties.cpp +++ b/taglib/xm/xmproperties.cpp @@ -60,7 +60,7 @@ public: XM::Properties::Properties(AudioProperties::ReadStyle propertiesStyle) : AudioProperties(propertiesStyle), - d(new PropertiesPrivate) + d(new PropertiesPrivate()) { } From 9b548260f501cd524db0b10336ed06394f8d29ed Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 16 Jan 2017 01:05:30 +0900 Subject: [PATCH 11/37] Initialize d-pointers in class member initializer list. --- taglib/mpeg/id3v2/frames/commentsframe.cpp | 5 +++-- taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp | 6 +++--- taglib/mpeg/id3v2/frames/privateframe.cpp | 5 +++-- taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp | 6 +++--- taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp | 6 +++--- taglib/mpeg/id3v2/id3v2extendedheader.cpp | 4 ++-- taglib/toolkit/tbytevectorstream.cpp | 4 ++-- taglib/toolkit/tfile.cpp | 9 ++++----- 8 files changed, 23 insertions(+), 22 deletions(-) diff --git a/taglib/mpeg/id3v2/frames/commentsframe.cpp b/taglib/mpeg/id3v2/frames/commentsframe.cpp index 7dda83dc..815e5e1a 100644 --- a/taglib/mpeg/id3v2/frames/commentsframe.cpp +++ b/taglib/mpeg/id3v2/frames/commentsframe.cpp @@ -190,8 +190,9 @@ ByteVector CommentsFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : Frame(h) +CommentsFrame::CommentsFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new CommentsFramePrivate()) { - d = new CommentsFramePrivate(); parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp index d543ab31..93031812 100644 --- a/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp +++ b/taglib/mpeg/id3v2/frames/eventtimingcodesframe.cpp @@ -136,9 +136,9 @@ ByteVector EventTimingCodesFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) - : Frame(h) +EventTimingCodesFrame::EventTimingCodesFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new EventTimingCodesFramePrivate()) { - d = new EventTimingCodesFramePrivate(); parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/privateframe.cpp b/taglib/mpeg/id3v2/frames/privateframe.cpp index 8c0a29ff..be80c4df 100644 --- a/taglib/mpeg/id3v2/frames/privateframe.cpp +++ b/taglib/mpeg/id3v2/frames/privateframe.cpp @@ -123,8 +123,9 @@ ByteVector PrivateFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : Frame(h) +PrivateFrame::PrivateFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new PrivateFramePrivate()) { - d = new PrivateFramePrivate(); parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp index e1559a72..5007629d 100644 --- a/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/synchronizedlyricsframe.cpp @@ -234,9 +234,9 @@ ByteVector SynchronizedLyricsFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) - : Frame(h) +SynchronizedLyricsFrame::SynchronizedLyricsFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new SynchronizedLyricsFramePrivate()) { - d = new SynchronizedLyricsFramePrivate(); parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 2e6c4bae..87317d32 100644 --- a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -190,9 +190,9 @@ ByteVector UnsynchronizedLyricsFrame::renderFields() const // private members //////////////////////////////////////////////////////////////////////////////// -UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) - : Frame(h) +UnsynchronizedLyricsFrame::UnsynchronizedLyricsFrame(const ByteVector &data, Header *h) : + Frame(h), + d(new UnsynchronizedLyricsFramePrivate()) { - d = new UnsynchronizedLyricsFramePrivate(); parseFields(fieldData(data)); } diff --git a/taglib/mpeg/id3v2/id3v2extendedheader.cpp b/taglib/mpeg/id3v2/id3v2extendedheader.cpp index 667e3e94..86eeee28 100644 --- a/taglib/mpeg/id3v2/id3v2extendedheader.cpp +++ b/taglib/mpeg/id3v2/id3v2extendedheader.cpp @@ -41,9 +41,9 @@ public: // public methods //////////////////////////////////////////////////////////////////////////////// -ExtendedHeader::ExtendedHeader() +ExtendedHeader::ExtendedHeader() : + d(new ExtendedHeaderPrivate()) { - d = new ExtendedHeaderPrivate(); } ExtendedHeader::~ExtendedHeader() diff --git a/taglib/toolkit/tbytevectorstream.cpp b/taglib/toolkit/tbytevectorstream.cpp index 74b2eced..333f528c 100644 --- a/taglib/toolkit/tbytevectorstream.cpp +++ b/taglib/toolkit/tbytevectorstream.cpp @@ -53,9 +53,9 @@ ByteVectorStream::ByteVectorStreamPrivate::ByteVectorStreamPrivate(const ByteVec // public members //////////////////////////////////////////////////////////////////////////////// -ByteVectorStream::ByteVectorStream(const ByteVector &data) +ByteVectorStream::ByteVectorStream(const ByteVector &data) : + d(new ByteVectorStreamPrivate(data)) { - d = new ByteVectorStreamPrivate(data); } ByteVectorStream::~ByteVectorStream() diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index c634baa8..897400b8 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -87,15 +87,14 @@ File::FilePrivate::FilePrivate(IOStream *stream, bool owner) : // public members //////////////////////////////////////////////////////////////////////////////// -File::File(FileName fileName) +File::File(FileName fileName) : + d(new FilePrivate(new FileStream(fileName), true)) { - IOStream *stream = new FileStream(fileName); - d = new FilePrivate(stream, true); } -File::File(IOStream *stream) +File::File(IOStream *stream) : + d(new FilePrivate(stream, false)) { - d = new FilePrivate(stream, false); } File::~File() From d64c833359018bb05c9afa50c06a84c22519aa1c Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 16 Jan 2017 01:14:35 +0900 Subject: [PATCH 12/37] Update NEWS. --- NEWS | 1 + 1 file changed, 1 insertion(+) diff --git a/NEWS b/NEWS index 774e6532..fd05c2cd 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,7 @@ * Fixed handling of redundant UTF-8 sequences in Win32. * Fixed handling of lowercase field names in Vorbis Comments. * Fixed possible file corruptions when saving Ogg files. + * Fixed reading FLAC files with zero-sized seektables. * Several smaller bug fixes and performance improvements. TagLib 1.11.1 (Oct 24, 2016) From d2e0e5522308ce4c905c205c7bf5972ea71cd6e5 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 20 Jan 2017 21:14:38 +0900 Subject: [PATCH 13/37] Efficient lookup for an ID3v2 tag in MPEG files with garbage. This looks for an ID3v2 tag until reaching the first valid MPEG frame in O(n) time. --- taglib/mpeg/mpegfile.cpp | 47 +++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index af7253fa..c634eeb8 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -488,28 +488,41 @@ long MPEG::File::findID3v2() const ByteVector headerID = ID3v2::Header::fileIdentifier(); seek(0); - - const ByteVector data = readBlock(headerID.size()); - if(data.size() < headerID.size()) - return -1; - - if(data == headerID) + if(readBlock(headerID.size()) == headerID) return 0; - if(firstSyncByte(data[0]) && secondSynchByte(data[1])) + Header firstHeader(this, 0, true); + if(firstHeader.isValid()) return -1; - // Look for the entire file, if neither an MEPG frame or ID3v2 tag was found - // at the beginning of the file. - // We don't care about the inefficiency of the code, since this is a seldom case. + // Look for an ID3v2 tag until reaching the first valid MPEG frame. - const long tagOffset = find(headerID); - if(tagOffset < 0) - return -1; + char frameSyncBytes[2] = {}; + char tagHeaderBytes[4] = {}; + long position = 0; - const long frameOffset = firstFrameOffset(); - if(frameOffset < tagOffset) - return -1; + while(true) { + seek(position); + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.isEmpty()) + return -1; - return tagOffset; + for(unsigned int i = 0; i < buffer.size(); ++i) { + frameSyncBytes[0] = frameSyncBytes[1]; + frameSyncBytes[1] = buffer[i]; + if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + Header header(this, position + i - 1, true); + if(header.isValid()) + return -1; + } + + tagHeaderBytes[0] = tagHeaderBytes[1]; + tagHeaderBytes[1] = tagHeaderBytes[2]; + tagHeaderBytes[2] = buffer[i]; + if(headerID == tagHeaderBytes) + return position + i - 2; + } + + position += bufferSize(); + } } From 6bb92c34fac889f0ca9667ed0d1c6cfda23a6938 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 20 Jan 2017 22:38:25 +0900 Subject: [PATCH 14/37] Ignore fake MPEG frame headers when seeking them. --- taglib/mpeg/mpegfile.cpp | 57 ++++++++++++++++----------------- taglib/mpeg/mpegproperties.cpp | 29 +++-------------- tests/data/garbage.mp3 | Bin 0 -> 8190 bytes tests/test_mpeg.cpp | 33 ++++++++++++++----- 4 files changed, 56 insertions(+), 63 deletions(-) create mode 100644 tests/data/garbage.mp3 diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index c634eeb8..6860053b 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -346,55 +346,52 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) long MPEG::File::nextFrameOffset(long position) { - bool foundLastSyncPattern = false; - - ByteVector buffer; + char frameSyncBytes[2] = {}; while(true) { seek(position); - buffer = readBlock(bufferSize()); - - if(buffer.size() <= 0) + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.size() == 0) return -1; - if(foundLastSyncPattern && secondSynchByte(buffer[0])) - return position - 1; - - for(unsigned int i = 0; i < buffer.size() - 1; i++) { - if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) - return position + i; + for(unsigned int i = 0; i < buffer.size(); ++i) { + frameSyncBytes[0] = frameSyncBytes[1]; + frameSyncBytes[1] = buffer[i]; + if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + Header header(this, position + i - 1, true); + if(header.isValid()) + return position + i - 1; + } } - foundLastSyncPattern = firstSyncByte(buffer[buffer.size() - 1]); - position += buffer.size(); + position += bufferSize(); } } long MPEG::File::previousFrameOffset(long position) { - bool foundFirstSyncPattern = false; - ByteVector buffer; + char frameSyncBytes[2] = {}; - while (position > 0) { - long size = std::min(position, bufferSize()); + while(position > 0) { + const long size = std::min(position, bufferSize()); position -= size; seek(position); - buffer = readBlock(size); + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.size() == 0) + return -1; - if(buffer.size() <= 0) - break; - - if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1])) - return position + buffer.size() - 1; - - for(int i = buffer.size() - 2; i >= 0; i--) { - if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) - return position + i; + for(int i = buffer.size() - 1; i >= 0; i--) { + frameSyncBytes[1] = frameSyncBytes[0]; + frameSyncBytes[0] = buffer[i]; + if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + Header header(this, position + i, true); + if(header.isValid()) + return position + i + header.frameLength(); + } } - - foundFirstSyncPattern = secondSynchByte(buffer[0]); } + return -1; } diff --git a/taglib/mpeg/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 6e7bb823..d8b7bd50 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -157,23 +157,13 @@ void MPEG::Properties::read(File *file) { // Only the first valid frame is required if we have a VBR header. - long firstFrameOffset = file->firstFrameOffset(); + const long firstFrameOffset = file->firstFrameOffset(); if(firstFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); return; } - Header firstHeader(file, firstFrameOffset); - - while(!firstHeader.isValid()) { - firstFrameOffset = file->nextFrameOffset(firstFrameOffset + 1); - if(firstFrameOffset < 0) { - debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream."); - return; - } - - firstHeader = Header(file, firstFrameOffset); - } + const Header firstHeader(file, firstFrameOffset, false); // Check for a VBR header that will help us in gathering information about a // VBR stream. @@ -207,24 +197,13 @@ void MPEG::Properties::read(File *file) // Look for the last MPEG audio frame to calculate the stream length. - long lastFrameOffset = file->lastFrameOffset(); + const long lastFrameOffset = file->lastFrameOffset(); if(lastFrameOffset < 0) { debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream."); return; } - Header lastHeader(file, lastFrameOffset, false); - - while(!lastHeader.isValid()) { - lastFrameOffset = file->previousFrameOffset(lastFrameOffset); - if(lastFrameOffset < 0) { - debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream."); - return; - } - - lastHeader = Header(file, lastFrameOffset, false); - } - + const Header lastHeader(file, lastFrameOffset, false); const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); if(streamLength > 0) d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); diff --git a/tests/data/garbage.mp3 b/tests/data/garbage.mp3 new file mode 100644 index 0000000000000000000000000000000000000000..730b74e7c2babe736acc8b5922e0a0fbc1f6f33f GIT binary patch literal 8190 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 YdjCZ_?Wr7v#yEfT4BEkpaZzjfUmgnumH+?% literal 0 HcmV?d00001 diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index c1179ee5..5a990580 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -64,6 +64,7 @@ class TestMPEG : public CppUnit::TestFixture CPPUNIT_TEST(testEmptyID3v2); CPPUNIT_TEST(testEmptyID3v1); CPPUNIT_TEST(testEmptyAPE); + CPPUNIT_TEST(testIgnoreGarbage); CPPUNIT_TEST_SUITE_END(); public: @@ -119,13 +120,8 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - long last = f.lastFrameOffset(); - MPEG::Header lastHeader(&f, last, false); - - while(!lastHeader.isValid()) { - last = f.previousFrameOffset(last); - lastHeader = MPEG::Header(&f, last, false); - } + const long last = f.lastFrameOffset(); + const MPEG::Header lastHeader(&f, last, false); CPPUNIT_ASSERT_EQUAL(28213L, last); CPPUNIT_ASSERT_EQUAL(209, lastHeader.frameLength()); @@ -163,7 +159,7 @@ public: CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->length()); CPPUNIT_ASSERT_EQUAL(0, f.audioProperties()->lengthInSeconds()); - CPPUNIT_ASSERT_EQUAL(176, f.audioProperties()->lengthInMilliseconds()); + CPPUNIT_ASSERT_EQUAL(183, f.audioProperties()->lengthInMilliseconds()); CPPUNIT_ASSERT_EQUAL(320, f.audioProperties()->bitrate()); CPPUNIT_ASSERT_EQUAL(2, f.audioProperties()->channels()); CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); @@ -421,6 +417,27 @@ public: } } + void testIgnoreGarbage() + { + const ScopedFileCopy copy("garbage", ".mp3"); + { + MPEG::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT_EQUAL(2255L, f.firstFrameOffset()); + CPPUNIT_ASSERT_EQUAL(6015L, f.lastFrameOffset()); + CPPUNIT_ASSERT_EQUAL(String("Title A"), f.ID3v2Tag()->title()); + f.ID3v2Tag()->setTitle("Title B"); + f.save(); + } + { + MPEG::File f(copy.fileName().c_str()); + CPPUNIT_ASSERT(f.isValid()); + CPPUNIT_ASSERT(f.hasID3v2Tag()); + CPPUNIT_ASSERT_EQUAL(String("Title B"), f.ID3v2Tag()->title()); + } + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestMPEG); From c4a3c3ab97b5b05a515375fcec124b275c22c394 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 21 Jan 2017 01:34:50 +0900 Subject: [PATCH 15/37] Combine two internal functions which are always used together. --- taglib/mpeg/mpegfile.cpp | 16 ++++++++-------- taglib/mpeg/mpegheader.cpp | 2 +- taglib/mpeg/mpegutils.h | 16 ++++++++-------- 3 files changed, 17 insertions(+), 17 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 6860053b..64cae2d3 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -346,7 +346,7 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) long MPEG::File::nextFrameOffset(long position) { - char frameSyncBytes[2] = {}; + ByteVector frameSyncBytes(2, '\0'); while(true) { seek(position); @@ -357,7 +357,7 @@ long MPEG::File::nextFrameOffset(long position) for(unsigned int i = 0; i < buffer.size(); ++i) { frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; - if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + if(isFrameSync(frameSyncBytes)) { Header header(this, position + i - 1, true); if(header.isValid()) return position + i - 1; @@ -370,7 +370,7 @@ long MPEG::File::nextFrameOffset(long position) long MPEG::File::previousFrameOffset(long position) { - char frameSyncBytes[2] = {}; + ByteVector frameSyncBytes(2, '\0'); while(position > 0) { const long size = std::min(position, bufferSize()); @@ -384,7 +384,7 @@ long MPEG::File::previousFrameOffset(long position) for(int i = buffer.size() - 1; i >= 0; i--) { frameSyncBytes[1] = frameSyncBytes[0]; frameSyncBytes[0] = buffer[i]; - if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + if(isFrameSync(frameSyncBytes)) { Header header(this, position + i, true); if(header.isValid()) return position + i + header.frameLength(); @@ -494,8 +494,8 @@ long MPEG::File::findID3v2() // Look for an ID3v2 tag until reaching the first valid MPEG frame. - char frameSyncBytes[2] = {}; - char tagHeaderBytes[4] = {}; + ByteVector frameSyncBytes(2, '\0'); + ByteVector tagHeaderBytes(3, '\0'); long position = 0; while(true) { @@ -507,7 +507,7 @@ long MPEG::File::findID3v2() for(unsigned int i = 0; i < buffer.size(); ++i) { frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; - if(firstSyncByte(frameSyncBytes[0]) && secondSynchByte(frameSyncBytes[1])) { + if(isFrameSync(frameSyncBytes)) { Header header(this, position + i - 1, true); if(header.isValid()) return -1; @@ -516,7 +516,7 @@ long MPEG::File::findID3v2() tagHeaderBytes[0] = tagHeaderBytes[1]; tagHeaderBytes[1] = tagHeaderBytes[2]; tagHeaderBytes[2] = buffer[i]; - if(headerID == tagHeaderBytes) + if(tagHeaderBytes == headerID) return position + i - 2; } diff --git a/taglib/mpeg/mpegheader.cpp b/taglib/mpeg/mpegheader.cpp index e678f15b..610b0320 100644 --- a/taglib/mpeg/mpegheader.cpp +++ b/taglib/mpeg/mpegheader.cpp @@ -182,7 +182,7 @@ void MPEG::Header::parse(File *file, long offset, bool checkLength) // Check for the MPEG synch bytes. - if(!firstSyncByte(data[0]) || !secondSynchByte(data[1])) { + if(!isFrameSync(data)) { debug("MPEG::Header::parse() -- MPEG header did not match MPEG synch."); return; } diff --git a/taglib/mpeg/mpegutils.h b/taglib/mpeg/mpegutils.h index e35f752f..1cee918a 100644 --- a/taglib/mpeg/mpegutils.h +++ b/taglib/mpeg/mpegutils.h @@ -41,17 +41,17 @@ namespace TagLib * MPEG frames can be recognized by the bit pattern 11111111 111, so the * first byte is easy to check for, however checking to see if the second byte * starts with \e 111 is a bit more tricky, hence these functions. + * + * \note This does not check the length of the vector, since this is an + * internal utility function. */ - inline bool firstSyncByte(unsigned char byte) + inline bool isFrameSync(const ByteVector &bytes) { - return (byte == 0xFF); - } + // 0xFF in the second byte is possible in theory, but it's very unlikely. - inline bool secondSynchByte(unsigned char byte) - { - // 0xFF is possible in theory, but it's very unlikely be a header. - - return (byte != 0xFF && ((byte & 0xE0) == 0xE0)); + const unsigned char b1 = bytes[0]; + const unsigned char b2 = bytes[1]; + return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0); } } From 5ba8b740f9b74d8352ff46c046ffbe901aee3615 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 21 Jan 2017 11:09:05 +0900 Subject: [PATCH 16/37] Add missing consts. --- taglib/mpeg/mpegfile.cpp | 8 ++++---- taglib/mpeg/mpegutils.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 64cae2d3..96f1cf42 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -358,7 +358,7 @@ long MPEG::File::nextFrameOffset(long position) frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; if(isFrameSync(frameSyncBytes)) { - Header header(this, position + i - 1, true); + const Header header(this, position + i - 1, true); if(header.isValid()) return position + i - 1; } @@ -385,7 +385,7 @@ long MPEG::File::previousFrameOffset(long position) frameSyncBytes[1] = frameSyncBytes[0]; frameSyncBytes[0] = buffer[i]; if(isFrameSync(frameSyncBytes)) { - Header header(this, position + i, true); + const Header header(this, position + i, true); if(header.isValid()) return position + i + header.frameLength(); } @@ -488,7 +488,7 @@ long MPEG::File::findID3v2() if(readBlock(headerID.size()) == headerID) return 0; - Header firstHeader(this, 0, true); + const Header firstHeader(this, 0, true); if(firstHeader.isValid()) return -1; @@ -508,7 +508,7 @@ long MPEG::File::findID3v2() frameSyncBytes[0] = frameSyncBytes[1]; frameSyncBytes[1] = buffer[i]; if(isFrameSync(frameSyncBytes)) { - Header header(this, position + i - 1, true); + const Header header(this, position + i - 1, true); if(header.isValid()) return -1; } diff --git a/taglib/mpeg/mpegutils.h b/taglib/mpeg/mpegutils.h index 1cee918a..8b8cfd69 100644 --- a/taglib/mpeg/mpegutils.h +++ b/taglib/mpeg/mpegutils.h @@ -45,12 +45,12 @@ namespace TagLib * \note This does not check the length of the vector, since this is an * internal utility function. */ - inline bool isFrameSync(const ByteVector &bytes) + inline bool isFrameSync(const ByteVector &bytes, long offset = 0) { // 0xFF in the second byte is possible in theory, but it's very unlikely. - const unsigned char b1 = bytes[0]; - const unsigned char b2 = bytes[1]; + const unsigned char b1 = bytes[offset + 0]; + const unsigned char b2 = bytes[offset + 1]; return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0); } From a358e87cc43a8ce0cfd13bf040ea72624ae8ed19 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 21 Jan 2017 11:13:49 +0900 Subject: [PATCH 17/37] Revert useless changes accidentally committed. --- taglib/mpeg/mpegutils.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/taglib/mpeg/mpegutils.h b/taglib/mpeg/mpegutils.h index 8b8cfd69..1cee918a 100644 --- a/taglib/mpeg/mpegutils.h +++ b/taglib/mpeg/mpegutils.h @@ -45,12 +45,12 @@ namespace TagLib * \note This does not check the length of the vector, since this is an * internal utility function. */ - inline bool isFrameSync(const ByteVector &bytes, long offset = 0) + inline bool isFrameSync(const ByteVector &bytes) { // 0xFF in the second byte is possible in theory, but it's very unlikely. - const unsigned char b1 = bytes[offset + 0]; - const unsigned char b2 = bytes[offset + 1]; + const unsigned char b1 = bytes[0]; + const unsigned char b2 = bytes[1]; return (b1 == 0xFF && b2 != 0xFF && (b2 & 0xE0) == 0xE0); } From 5fc5a2e81a874c0d771e31c450d40ca4a329dc9c Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 21 Jan 2017 19:10:32 +0900 Subject: [PATCH 18/37] Prefer isEmpty()/empty() to size() == 0. --- taglib/mpeg/mpegfile.cpp | 4 ++-- taglib/riff/rifffile.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index 96f1cf42..b8ff40dd 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -351,7 +351,7 @@ long MPEG::File::nextFrameOffset(long position) while(true) { seek(position); const ByteVector buffer = readBlock(bufferSize()); - if(buffer.size() == 0) + if(buffer.isEmpty()) return -1; for(unsigned int i = 0; i < buffer.size(); ++i) { @@ -378,7 +378,7 @@ long MPEG::File::previousFrameOffset(long position) seek(position); const ByteVector buffer = readBlock(bufferSize()); - if(buffer.size() == 0) + if(buffer.isEmpty()) return -1; for(int i = buffer.size() - 1; i >= 0; i--) { diff --git a/taglib/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index 51c9af41..b03e06f9 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -187,7 +187,7 @@ void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data) void RIFF::File::setChunkData(const ByteVector &name, const ByteVector &data, bool alwaysCreate) { - if(d->chunks.size() == 0) { + if(d->chunks.empty()) { debug("RIFF::File::setChunkData - No valid chunks found."); return; } From fc38a0e40131f649fba37e218da2d9598ef3a633 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sun, 22 Jan 2017 00:43:32 +0900 Subject: [PATCH 19/37] Remove some redundant code. TagUnion::access() does the same thing as FLAC::File::ID3v2Tag(). --- taglib/flac/flacfile.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index 34599e89..b6b72960 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -297,11 +297,7 @@ bool FLAC::File::save() ID3v2::Tag *FLAC::File::ID3v2Tag(bool create) { - if(!create || d->tag[FlacID3v2Index]) - return static_cast(d->tag[FlacID3v2Index]); - - d->tag.set(FlacID3v2Index, new ID3v2::Tag()); - return static_cast(d->tag[FlacID3v2Index]); + return d->tag.access(FlacID3v2Index, create); } ID3v1::Tag *FLAC::File::ID3v1Tag(bool create) From 586c9bd962f0c3f9aa520d7f2f9bf0f7447a9cbf Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 26 Jan 2017 17:33:54 +0900 Subject: [PATCH 20/37] Add a test for unpaired surrogate characters in a UTF-16 string. --- tests/test_string.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 4e3a1ac5..d3661c24 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -176,6 +176,10 @@ public: const String s2(v2, String::UTF8); CPPUNIT_ASSERT_EQUAL(s2.data(String::UTF16), v1); + + const ByteVector v3("\xff\xfe\xb7\xdf\xce\x91\x4b\x5c"); + const String s3(v2, String::UTF16); + CPPUNIT_ASSERT(s3.isEmpty()); } void testAppendStringDetach() From 0c45c63943ec70bcb8734299ca0b86f4f0131c6f Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 27 Jan 2017 11:44:56 +0900 Subject: [PATCH 21/37] Replace unicode.h/unicode.cpp by the UTF8-CPP library. unicode.h/unicode.cpp are no longer maintained and incompatible with Debian's guideline. UTF8-CPP is maintained on GitHub and published under the Boost Software License which is compatible with either LGPL or MPL and should go along with Debian's guideline. --- taglib/CMakeLists.txt | 8 +- taglib/toolkit/tstring.cpp | 100 ++--------- taglib/toolkit/unicode.cpp | 303 ------------------------------- taglib/toolkit/unicode.h | 149 --------------- taglib/toolkit/utf8/checked.h | 327 +++++++++++++++++++++++++++++++++ taglib/toolkit/utf8/core.h | 329 ++++++++++++++++++++++++++++++++++ tests/test_string.cpp | 8 + 7 files changed, 682 insertions(+), 542 deletions(-) delete mode 100644 taglib/toolkit/unicode.cpp delete mode 100644 taglib/toolkit/unicode.h create mode 100644 taglib/toolkit/utf8/checked.h create mode 100644 taglib/toolkit/utf8/core.h diff --git a/taglib/CMakeLists.txt b/taglib/CMakeLists.txt index 4ea54304..13ba7892 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -312,12 +312,6 @@ set(toolkit_SRCS toolkit/tzlib.cpp ) -if(NOT WIN32) - set(unicode_SRCS - toolkit/unicode.cpp - ) -endif() - if(HAVE_ZLIB_SOURCE) set(zlib_SRCS ${ZLIB_SOURCE}/adler32.c @@ -334,7 +328,7 @@ set(tag_LIB_SRCS ${vorbis_SRCS} ${oggflacs_SRCS} ${mpc_SRCS} ${ape_SRCS} ${toolkit_SRCS} ${flacs_SRCS} ${wavpack_SRCS} ${speex_SRCS} ${trueaudio_SRCS} ${riff_SRCS} ${aiff_SRCS} ${wav_SRCS} ${asf_SRCS} ${mp4_SRCS} ${mod_SRCS} ${s3m_SRCS} ${it_SRCS} ${xm_SRCS} ${opus_SRCS} - ${unicode_SRCS} ${zlib_SRCS} + ${zlib_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 1639d7c5..b154cbae 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -31,16 +31,11 @@ #include #include -#ifdef _WIN32 -# include -#else -# include "unicode.h" -#endif - #include #include #include #include +#include #include "tstring.h" @@ -48,72 +43,6 @@ namespace { using namespace TagLib; - size_t UTF16toUTF8(const wchar_t *src, size_t srcLength, char *dst, size_t dstLength) - { - size_t len = 0; - -#ifdef _WIN32 - - len = ::WideCharToMultiByte( - CP_UTF8, 0, src, static_cast(srcLength), dst, static_cast(dstLength), NULL, NULL); - -#else - - using namespace Unicode; - - const UTF16 *srcBegin = src; - const UTF16 *srcEnd = srcBegin + srcLength; - - UTF8 *dstBegin = reinterpret_cast(dst); - UTF8 *dstEnd = dstBegin + dstLength; - - ConversionResult result = ConvertUTF16toUTF8( - &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - - if(result == conversionOK) - len = dstBegin - reinterpret_cast(dst); - -#endif - - if(len == 0) - debug("String::UTF16toUTF8() - Unicode conversion error."); - - return len; - } - - size_t UTF8toUTF16(const char *src, size_t srcLength, wchar_t *dst, size_t dstLength) - { - size_t len = 0; - -#ifdef _WIN32 - - len = ::MultiByteToWideChar( - CP_UTF8, MB_ERR_INVALID_CHARS, src, static_cast(srcLength), dst, static_cast(dstLength)); - -#else - - using namespace Unicode; - - const UTF8 *srcBegin = reinterpret_cast(src); - const UTF8 *srcEnd = srcBegin + srcLength; - - UTF16 *dstBegin = dst; - UTF16 *dstEnd = dstBegin + dstLength; - - ConversionResult result = ConvertUTF8toUTF16( - &srcBegin, srcEnd, &dstBegin, dstEnd, lenientConversion); - - if(result == conversionOK) - len = dstBegin - dst; - -#endif - - if(len == 0) - debug("String::UTF8toUTF16() - Unicode conversion error."); - - return len; - } - // Returns the native format of std::wstring. String::Type wcharByteOrder() { @@ -139,9 +68,13 @@ namespace { data.resize(length); - if(length > 0) { - const size_t len = UTF8toUTF16(s, length, &data[0], data.size()); - data.resize(len); + try { + const std::wstring::iterator dstEnd = utf8::utf8to16(s, s + length, data.begin()); + data.resize(dstEnd - data.begin()); + } + catch(const utf8::exception &e) { + debug(String("String::copyFromUTF8() - UTF8-CPP error: ") + e.what()); + data.clear(); } } @@ -517,19 +450,20 @@ ByteVector String::data(Type t) const return v; } case UTF8: - if(!d->data.empty()) { - ByteVector v(size() * 4 + 1, 0); + ByteVector v(size() * 4, 0); - const size_t len = UTF16toUTF8( - d->data.c_str(), d->data.size(), v.data(), v.size()); - v.resize(static_cast(len)); + try { + const ByteVector::Iterator dstEnd = utf8::utf16to8(begin(), end(), v.begin()); + v.resize(static_cast(dstEnd - v.begin())); + } + catch(const utf8::exception &e) { + debug(String("String::data() - UTF8-CPP error: ") + e.what()); + v.clear(); + } return v; } - else { - return ByteVector(); - } case UTF16: { ByteVector v(2 + size() * 2, 0); diff --git a/taglib/toolkit/unicode.cpp b/taglib/toolkit/unicode.cpp deleted file mode 100644 index 1b26977e..00000000 --- a/taglib/toolkit/unicode.cpp +++ /dev/null @@ -1,303 +0,0 @@ -/******************************************************************************* - * * - * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB * - * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. * - * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. * - * * - *******************************************************************************/ - -/* - * Copyright 2001 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* - * This file has been modified by Scott Wheeler to remove - * the UTF32 conversion functions and to place the appropriate functions - * in their own C++ namespace. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Source code file. - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Sept 2001: fixed const & error conditions per - mods suggested by S. Parent & A. Lillich. - - See the header file "ConvertUTF.h" for complete documentation. - ------------------------------------------------------------------------- */ - - -#include "unicode.h" -#include - -#define UNI_SUR_HIGH_START (UTF32)0xD800 -#define UNI_SUR_HIGH_END (UTF32)0xDBFF -#define UNI_SUR_LOW_START (UTF32)0xDC00 -#define UNI_SUR_LOW_END (UTF32)0xDFFF -#define false 0 -#define true 1 - -namespace Unicode { - -static const int halfShift = 10; /* used for shifting by 10 bits */ - -static const UTF32 halfBase = 0x0010000UL; -static const UTF32 halfMask = 0x3FFUL; - -/* - * Index into the table below with the first byte of a UTF-8 sequence to - * get the number of trailing bytes that are supposed to follow it. - */ -static const char trailingBytesForUTF8[256] = { - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, - 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, - 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 -}; - -/* - * Magic values subtracted from a buffer value during UTF8 conversion. - * This table contains as many values as there might be trailing bytes - * in a UTF-8 sequence. - */ -static const UTF32 offsetsFromUTF8[6] = { 0x00000000UL, 0x00003080UL, 0x000E2080UL, - 0x03C82080UL, 0xFA082080UL, 0x82082080UL }; - -/* - * Once the bits are split out into bytes of UTF-8, this is a mask OR-ed - * into the first byte, depending on how many bytes follow. There are - * as many entries in this table as there are UTF-8 sequence types. - * (I.e., one byte sequence, two byte... six byte sequence.) - */ -static const UTF8 firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; - -/* --------------------------------------------------------------------- */ - -/* The interface converts a whole buffer to avoid function-call overhead. - * Constants have been gathered. Loops & conditionals have been removed as - * much as possible for efficiency, in favor of drop-through switches. - * (See "Note A" at the bottom of the file for equivalent code.) - * If your compiler supports it, the "isLegalUTF8" call can be turned - * into an inline function. - */ - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF16* source = *sourceStart; - UTF8* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch; - unsigned short bytesToWrite = 0; - const UTF32 byteMask = 0xBF; - const UTF32 byteMark = 0x80; - const UTF16* oldSource = source; /* In case we have to back up because of target overflow. */ - ch = *source++; - /* If we have a surrogate pair, convert to UTF32 first. */ - if (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_HIGH_END && source < sourceEnd) { - UTF32 ch2 = *source; - if (ch2 >= UNI_SUR_LOW_START && ch2 <= UNI_SUR_LOW_END) { - ch = ((ch - UNI_SUR_HIGH_START) << halfShift) - + (ch2 - UNI_SUR_LOW_START) + halfBase; - ++source; - } else if (flags == strictConversion) { /* it's an unpaired high surrogate */ - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - } else if ((flags == strictConversion) && (ch >= UNI_SUR_LOW_START && ch <= UNI_SUR_LOW_END)) { - --source; /* return to the illegal value itself */ - result = sourceIllegal; - break; - } - /* Figure out how many bytes the result will require */ - if (ch < (UTF32)0x80) { bytesToWrite = 1; - } else if (ch < (UTF32)0x800) { bytesToWrite = 2; - } else if (ch < (UTF32)0x10000) { bytesToWrite = 3; - } else if (ch < (UTF32)0x200000) { bytesToWrite = 4; - } else { bytesToWrite = 2; - ch = UNI_REPLACEMENT_CHAR; - } - // printf("bytes to write = %i\n", bytesToWrite); - target += bytesToWrite; - if (target > targetEnd) { - source = oldSource; /* Back up source pointer! */ - target -= bytesToWrite; result = targetExhausted; break; - } - switch (bytesToWrite) { /* note: everything falls through. */ - case 4: *--target = (ch | byteMark) & byteMask; ch >>= 6; - case 3: *--target = (ch | byteMark) & byteMask; ch >>= 6; - case 2: *--target = (ch | byteMark) & byteMask; ch >>= 6; - case 1: *--target = (UTF8)(ch | firstByteMark[bytesToWrite]); - } - target += bytesToWrite; - } - *sourceStart = source; - *targetStart = target; - return result; -} - -/* --------------------------------------------------------------------- */ - -/* - * Utility routine to tell whether a sequence of bytes is legal UTF-8. - * This must be called with the length pre-determined by the first byte. - * If not calling this from ConvertUTF8to*, then the length can be set by: - * length = trailingBytesForUTF8[*source]+1; - * and the sequence is illegal right away if there aren't that many bytes - * available. - * If presented with a length > 4, this returns false. The Unicode - * definition of UTF-8 goes up to 4-byte sequences. - */ - -static Boolean isLegalUTF8(const UTF8 *source, int length) { - UTF8 a; - const UTF8 *srcptr = source+length; - switch (length) { - default: return false; - /* Everything else falls through when "true"... */ - case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return false; - case 2: if ((a = (*--srcptr)) > 0xBF) return false; - switch (*source) { - /* no fall-through in this inner switch */ - case 0xE0: if (a < 0xA0) return false; break; - case 0xF0: if (a < 0x90) return false; break; - case 0xF4: if (a > 0x8F) return false; break; - default: if (a < 0x80) return false; - } - case 1: if (*source >= 0x80 && *source < 0xC2) return false; - if (*source > 0xF4) return false; - } - return true; -} - -/* --------------------------------------------------------------------- */ - -/* - * Exported function to return whether a UTF-8 sequence is legal or not. - * This is not used here; it's just exported. - */ -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd) { - int length = trailingBytesForUTF8[*source]+1; - if (source+length > sourceEnd) { - return false; - } - return isLegalUTF8(source, length); -} - -/* --------------------------------------------------------------------- */ - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags) { - ConversionResult result = conversionOK; - const UTF8* source = *sourceStart; - UTF16* target = *targetStart; - while (source < sourceEnd) { - UTF32 ch = 0; - unsigned short extraBytesToRead = trailingBytesForUTF8[*source]; - if (source + extraBytesToRead >= sourceEnd) { - result = sourceExhausted; break; - } - /* Do this check whether lenient or strict */ - if (! isLegalUTF8(source, extraBytesToRead+1)) { - result = sourceIllegal; - break; - } - /* - * The cases all fall through. See "Note A" below. - */ - switch (extraBytesToRead) { - case 3: ch += *source++; ch <<= 6; - case 2: ch += *source++; ch <<= 6; - case 1: ch += *source++; ch <<= 6; - case 0: ch += *source++; - } - ch -= offsetsFromUTF8[extraBytesToRead]; - - if (target >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - if (ch <= UNI_MAX_BMP) { /* Target is a character <= 0xFFFF */ - if ((flags == strictConversion) && (ch >= UNI_SUR_HIGH_START && ch <= UNI_SUR_LOW_END)) { - source -= (extraBytesToRead+1); /* return to the illegal value itself */ - result = sourceIllegal; - break; - } else { - *target++ = (UTF16)ch; /* normal case */ - } - } else if (ch > UNI_MAX_UTF16) { - if (flags == strictConversion) { - result = sourceIllegal; - source -= (extraBytesToRead+1); /* return to the start */ - break; /* Bail out; shouldn't continue */ - } else { - *target++ = UNI_REPLACEMENT_CHAR; - } - } else { - /* target is a character in range 0xFFFF - 0x10FFFF. */ - if (target + 1 >= targetEnd) { - source -= (extraBytesToRead+1); /* Back up source pointer! */ - result = targetExhausted; break; - } - ch -= halfBase; - *target++ = (UTF16)((ch >> halfShift) + UNI_SUR_HIGH_START); - *target++ = (ch & halfMask) + UNI_SUR_LOW_START; - } - } - *sourceStart = source; - *targetStart = target; - return result; -} - -} - -/* --------------------------------------------------------------------- - - Note A. - The fall-through switches in UTF-8 reading code save a - temp variable, some decrements & conditionals. The switches - are equivalent to the following loop: - { - int tmpBytesToRead = extraBytesToRead+1; - do { - ch += *source++; - --tmpBytesToRead; - if (tmpBytesToRead) ch <<= 6; - } while (tmpBytesToRead > 0); - } - In UTF-8 writing code, the switches on "bytesToWrite" are - similarly unrolled loops. - - --------------------------------------------------------------------- */ - - diff --git a/taglib/toolkit/unicode.h b/taglib/toolkit/unicode.h deleted file mode 100644 index d3a869f0..00000000 --- a/taglib/toolkit/unicode.h +++ /dev/null @@ -1,149 +0,0 @@ -#ifndef TAGLIB_UNICODE_H -#define TAGLIB_UNICODE_H - -/******************************************************************************* - * * - * THIS FILE IS INCLUDED IN TAGLIB, BUT IS NOT COPYRIGHTED BY THE TAGLIB * - * AUTHORS, NOT PART OF THE TAGLIB API AND COULD GO AWAY AT ANY POINT IN TIME. * - * AS SUCH IT SHOULD BE CONSIERED FOR INTERNAL USE ONLY. * - * * - *******************************************************************************/ - -#ifndef DO_NOT_DOCUMENT // tell Doxygen not to document this header - -/* - * Copyright 2001 Unicode, Inc. - * - * Disclaimer - * - * This source code is provided as is by Unicode, Inc. No claims are - * made as to fitness for any particular purpose. No warranties of any - * kind are expressed or implied. The recipient agrees to determine - * applicability of information provided. If this file has been - * purchased on magnetic or optical media from Unicode, Inc., the - * sole remedy for any claim will be exchange of defective media - * within 90 days of receipt. - * - * Limitations on Rights to Redistribute This Code - * - * Unicode, Inc. hereby grants the right to freely use the information - * supplied in this file in the creation of products supporting the - * Unicode Standard, and to make copies of this file in any form - * for internal or external distribution as long as this notice - * remains attached. - */ - -/* - * This file has been modified by Scott Wheeler to remove - * the UTF32 conversion functions and to place the appropriate functions - * in their own C++ namespace. - */ - -/* --------------------------------------------------------------------- - - Conversions between UTF32, UTF-16, and UTF-8. Header file. - - Several functions are included here, forming a complete set of - conversions between the three formats. UTF-7 is not included - here, but is handled in a separate source file. - - Each of these routines takes pointers to input buffers and output - buffers. The input buffers are const. - - Each routine converts the text between *sourceStart and sourceEnd, - putting the result into the buffer between *targetStart and - targetEnd. Note: the end pointers are *after* the last item: e.g. - *(sourceEnd - 1) is the last item. - - The return result indicates whether the conversion was successful, - and if not, whether the problem was in the source or target buffers. - (Only the first encountered problem is indicated.) - - After the conversion, *sourceStart and *targetStart are both - updated to point to the end of last text successfully converted in - the respective buffers. - - Input parameters: - sourceStart - pointer to a pointer to the source buffer. - The contents of this are modified on return so that - it points at the next thing to be converted. - targetStart - similarly, pointer to pointer to the target buffer. - sourceEnd, targetEnd - respectively pointers to the ends of the - two buffers, for overflow checking only. - - These conversion functions take a ConversionFlags argument. When this - flag is set to strict, both irregular sequences and isolated surrogates - will cause an error. When the flag is set to lenient, both irregular - sequences and isolated surrogates are converted. - - Whether the flag is strict or lenient, all illegal sequences will cause - an error return. This includes sequences such as: , , - or in UTF-8, and values above 0x10FFFF in UTF-32. Conformant code - must check for illegal sequences. - - When the flag is set to lenient, characters over 0x10FFFF are converted - to the replacement character; otherwise (when the flag is set to strict) - they constitute an error. - - Output parameters: - The value "sourceIllegal" is returned from some routines if the input - sequence is malformed. When "sourceIllegal" is returned, the source - value will point to the illegal value that caused the problem. E.g., - in UTF-8 when a sequence is malformed, it points to the start of the - malformed sequence. - - Author: Mark E. Davis, 1994. - Rev History: Rick McGowan, fixes & updates May 2001. - Fixes & updates, Sept 2001. - ------------------------------------------------------------------------- */ - -/* --------------------------------------------------------------------- - The following 4 definitions are compiler-specific. - The C standard does not guarantee that wchar_t has at least - 16 bits, so wchar_t is no less portable than unsigned short! - All should be unsigned values to avoid sign extension during - bit mask & shift operations. ------------------------------------------------------------------------- */ - -/* Some fundamental constants */ -#define UNI_REPLACEMENT_CHAR (UTF32)0x0000FFFD -#define UNI_MAX_BMP (UTF32)0x0000FFFF -#define UNI_MAX_UTF16 (UTF32)0x0010FFFF -#define UNI_MAX_UTF32 (UTF32)0x7FFFFFFF - -namespace Unicode { - -typedef unsigned long UTF32; /* at least 32 bits */ -typedef wchar_t UTF16; /* TagLib assumes that wchar_t is sufficient for UTF-16. */ -typedef unsigned char UTF8; /* typically 8 bits */ -typedef unsigned char Boolean; /* 0 or 1 */ - -typedef enum { - conversionOK = 0, /* conversion successful */ - sourceExhausted = 1, /* partial character in source, but hit end */ - targetExhausted = 2, /* insuff. room in target for conversion */ - sourceIllegal = 3 /* source sequence is illegal/malformed */ -} ConversionResult; - -typedef enum { - strictConversion = 0, - lenientConversion -} ConversionFlags; - -ConversionResult ConvertUTF8toUTF16 ( - const UTF8** sourceStart, const UTF8* sourceEnd, - UTF16** targetStart, UTF16* targetEnd, ConversionFlags flags); - -ConversionResult ConvertUTF16toUTF8 ( - const UTF16** sourceStart, const UTF16* sourceEnd, - UTF8** targetStart, UTF8* targetEnd, ConversionFlags flags); - -Boolean isLegalUTF8Sequence(const UTF8 *source, const UTF8 *sourceEnd); - -} // namespace Unicode - -/* --------------------------------------------------------------------- */ - -#endif -#endif diff --git a/taglib/toolkit/utf8/checked.h b/taglib/toolkit/utf8/checked.h new file mode 100644 index 00000000..13311551 --- /dev/null +++ b/taglib/toolkit/utf8/checked.h @@ -0,0 +1,327 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CHECKED_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include "core.h" +#include + +namespace utf8 +{ + // Base for the exceptions that may be thrown from the library + class exception : public ::std::exception { + }; + + // Exceptions that may be thrown from the library functions. + class invalid_code_point : public exception { + uint32_t cp; + public: + invalid_code_point(uint32_t cp) : cp(cp) {} + virtual const char* what() const throw() { return "Invalid code point"; } + uint32_t code_point() const {return cp;} + }; + + class invalid_utf8 : public exception { + uint8_t u8; + public: + invalid_utf8 (uint8_t u) : u8(u) {} + virtual const char* what() const throw() { return "Invalid UTF-8"; } + uint8_t utf8_octet() const {return u8;} + }; + + class invalid_utf16 : public exception { + uint16_t u16; + public: + invalid_utf16 (uint16_t u) : u16(u) {} + virtual const char* what() const throw() { return "Invalid UTF-16"; } + uint16_t utf16_word() const {return u16;} + }; + + class not_enough_room : public exception { + public: + virtual const char* what() const throw() { return "Not enough space"; } + }; + + /// The library API - functions intended to be called by the users + + template + octet_iterator append(uint32_t cp, octet_iterator result) + { + if (!utf8::internal::is_code_point_valid(cp)) + throw invalid_code_point(cp); + + if (cp < 0x80) // one octet + *(result++) = static_cast(cp); + else if (cp < 0x800) { // two octets + *(result++) = static_cast((cp >> 6) | 0xc0); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else if (cp < 0x10000) { // three octets + *(result++) = static_cast((cp >> 12) | 0xe0); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + else { // four octets + *(result++) = static_cast((cp >> 18) | 0xf0); + *(result++) = static_cast(((cp >> 12) & 0x3f) | 0x80); + *(result++) = static_cast(((cp >> 6) & 0x3f) | 0x80); + *(result++) = static_cast((cp & 0x3f) | 0x80); + } + return result; + } + + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) { + case internal::UTF8_OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + throw not_enough_room(); + case internal::INVALID_LEAD: + out = utf8::append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } + } + return out; + } + + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::replace_invalid(start, end, out, replacement_marker); + } + + template + uint32_t next(octet_iterator& it, octet_iterator end) + { + uint32_t cp = 0; + internal::utf_error err_code = utf8::internal::validate_next(it, end, cp); + switch (err_code) { + case internal::UTF8_OK : + break; + case internal::NOT_ENOUGH_ROOM : + throw not_enough_room(); + case internal::INVALID_LEAD : + case internal::INCOMPLETE_SEQUENCE : + case internal::OVERLONG_SEQUENCE : + throw invalid_utf8(*it); + case internal::INVALID_CODE_POINT : + throw invalid_code_point(cp); + } + return cp; + } + + template + uint32_t peek_next(octet_iterator it, octet_iterator end) + { + return utf8::next(it, end); + } + + template + uint32_t prior(octet_iterator& it, octet_iterator start) + { + // can't do much if it == start + if (it == start) + throw not_enough_room(); + + octet_iterator end = it; + // Go back until we hit either a lead octet or start + while (utf8::internal::is_trail(*(--it))) + if (it == start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + return utf8::peek_next(it, end); + } + + /// Deprecated in versions that include "prior" + template + uint32_t previous(octet_iterator& it, octet_iterator pass_start) + { + octet_iterator end = it; + while (utf8::internal::is_trail(*(--it))) + if (it == pass_start) + throw invalid_utf8(*it); // error - no lead byte in the sequence + octet_iterator temp = it; + return utf8::next(temp, end); + } + + template + void advance (octet_iterator& it, distance_type n, octet_iterator end) + { + for (distance_type i = 0; i < n; ++i) + utf8::next(it, end); + } + + template + typename std::iterator_traits::difference_type + distance (octet_iterator first, octet_iterator last) + { + typename std::iterator_traits::difference_type dist; + for (dist = 0; first < last; ++dist) + utf8::next(first, last); + return dist; + } + + template + octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) + { + while (start != end) { + uint32_t cp = utf8::internal::mask16(*start++); + // Take care of surrogate pairs first + if (utf8::internal::is_lead_surrogate(cp)) { + if (start != end) { + uint32_t trail_surrogate = utf8::internal::mask16(*start++); + if (utf8::internal::is_trail_surrogate(trail_surrogate)) + cp = (cp << 10) + trail_surrogate + internal::SURROGATE_OFFSET; + else + throw invalid_utf16(static_cast(trail_surrogate)); + } + else + throw invalid_utf16(static_cast(cp)); + + } + // Lone trail surrogate + else if (utf8::internal::is_trail_surrogate(cp)) + throw invalid_utf16(static_cast(cp)); + + result = utf8::append(cp, result); + } + return result; + } + + template + u16bit_iterator utf8to16 (octet_iterator start, octet_iterator end, u16bit_iterator result) + { + while (start != end) { + uint32_t cp = utf8::next(start, end); + if (cp > 0xffff) { //make a surrogate pair + *result++ = static_cast((cp >> 10) + internal::LEAD_OFFSET); + *result++ = static_cast((cp & 0x3ff) + internal::TRAIL_SURROGATE_MIN); + } + else + *result++ = static_cast(cp); + } + return result; + } + + template + octet_iterator utf32to8 (u32bit_iterator start, u32bit_iterator end, octet_iterator result) + { + while (start != end) + result = utf8::append(*(start++), result); + + return result; + } + + template + u32bit_iterator utf8to32 (octet_iterator start, octet_iterator end, u32bit_iterator result) + { + while (start != end) + (*result++) = utf8::next(start, end); + + return result; + } + + // The iterator class + template + class iterator : public std::iterator { + octet_iterator it; + octet_iterator range_start; + octet_iterator range_end; + public: + iterator () {} + explicit iterator (const octet_iterator& octet_it, + const octet_iterator& range_start, + const octet_iterator& range_end) : + it(octet_it), range_start(range_start), range_end(range_end) + { + if (it < range_start || it > range_end) + throw std::out_of_range("Invalid utf-8 iterator position"); + } + // the default "big three" are OK + octet_iterator base () const { return it; } + uint32_t operator * () const + { + octet_iterator temp = it; + return utf8::next(temp, range_end); + } + bool operator == (const iterator& rhs) const + { + if (range_start != rhs.range_start || range_end != rhs.range_end) + throw std::logic_error("Comparing utf-8 iterators defined with different ranges"); + return (it == rhs.it); + } + bool operator != (const iterator& rhs) const + { + return !(operator == (rhs)); + } + iterator& operator ++ () + { + utf8::next(it, range_end); + return *this; + } + iterator operator ++ (int) + { + iterator temp = *this; + utf8::next(it, range_end); + return temp; + } + iterator& operator -- () + { + utf8::prior(it, range_start); + return *this; + } + iterator operator -- (int) + { + iterator temp = *this; + utf8::prior(it, range_start); + return temp; + } + }; // class iterator + +} // namespace utf8 + +#endif //header guard + + diff --git a/taglib/toolkit/utf8/core.h b/taglib/toolkit/utf8/core.h new file mode 100644 index 00000000..693d388c --- /dev/null +++ b/taglib/toolkit/utf8/core.h @@ -0,0 +1,329 @@ +// Copyright 2006 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 +#define UTF8_FOR_CPP_CORE_H_2675DCD0_9480_4c0c_B92A_CC14C027B731 + +#include + +namespace utf8 +{ + // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers + // You may need to change them to match your system. + // These typedefs have the same names as ones from cstdint, or boost/cstdint + typedef unsigned char uint8_t; + typedef unsigned short uint16_t; + typedef unsigned int uint32_t; + +// Helper code - not intended to be directly called by the library users. May be changed at any time +namespace internal +{ + // Unicode constants + // Leading (high) surrogates: 0xd800 - 0xdbff + // Trailing (low) surrogates: 0xdc00 - 0xdfff + const uint16_t LEAD_SURROGATE_MIN = 0xd800u; + const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; + const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; + const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; + const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); + const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + + // Maximum valid value for a Unicode code point + const uint32_t CODE_POINT_MAX = 0x0010ffffu; + + template + inline uint8_t mask8(octet_type oc) + { + return static_cast(0xff & oc); + } + template + inline uint16_t mask16(u16_type oc) + { + return static_cast(0xffff & oc); + } + template + inline bool is_trail(octet_type oc) + { + return ((utf8::internal::mask8(oc) >> 6) == 0x2); + } + + template + inline bool is_lead_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= LEAD_SURROGATE_MAX); + } + + template + inline bool is_trail_surrogate(u16 cp) + { + return (cp >= TRAIL_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template + inline bool is_surrogate(u16 cp) + { + return (cp >= LEAD_SURROGATE_MIN && cp <= TRAIL_SURROGATE_MAX); + } + + template + inline bool is_code_point_valid(u32 cp) + { + return (cp <= CODE_POINT_MAX && !utf8::internal::is_surrogate(cp)); + } + + template + inline typename std::iterator_traits::difference_type + sequence_length(octet_iterator lead_it) + { + uint8_t lead = utf8::internal::mask8(*lead_it); + if (lead < 0x80) + return 1; + else if ((lead >> 5) == 0x6) + return 2; + else if ((lead >> 4) == 0xe) + return 3; + else if ((lead >> 3) == 0x1e) + return 4; + else + return 0; + } + + template + inline bool is_overlong_sequence(uint32_t cp, octet_difference_type length) + { + if (cp < 0x80) { + if (length != 1) + return true; + } + else if (cp < 0x800) { + if (length != 2) + return true; + } + else if (cp < 0x10000) { + if (length != 3) + return true; + } + + return false; + } + + enum utf_error {UTF8_OK, NOT_ENOUGH_ROOM, INVALID_LEAD, INCOMPLETE_SEQUENCE, OVERLONG_SEQUENCE, INVALID_CODE_POINT}; + + /// Helper for get_sequence_x + template + utf_error increase_safely(octet_iterator& it, octet_iterator end) + { + if (++it == end) + return NOT_ENOUGH_ROOM; + + if (!utf8::internal::is_trail(*it)) + return INCOMPLETE_SEQUENCE; + + return UTF8_OK; + } + + #define UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(IT, END) {utf_error ret = increase_safely(IT, END); if (ret != UTF8_OK) return ret;} + + /// get_sequence_x functions decode utf-8 sequences of the length x + template + utf_error get_sequence_1(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + return UTF8_OK; + } + + template + utf_error get_sequence_2(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 6) & 0x7ff) + ((*it) & 0x3f); + + return UTF8_OK; + } + + template + utf_error get_sequence_3(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 12) & 0xffff) + ((utf8::internal::mask8(*it) << 6) & 0xfff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + template + utf_error get_sequence_4(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + if (it == end) + return NOT_ENOUGH_ROOM; + + code_point = utf8::internal::mask8(*it); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point = ((code_point << 18) & 0x1fffff) + ((utf8::internal::mask8(*it) << 12) & 0x3ffff); + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (utf8::internal::mask8(*it) << 6) & 0xfff; + + UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) + + code_point += (*it) & 0x3f; + + return UTF8_OK; + } + + #undef UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR + + template + utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) + { + // Save the original value of it so we can go back in case of failure + // Of course, it does not make much sense with i.e. stream iterators + octet_iterator original_it = it; + + uint32_t cp = 0; + // Determine the sequence length based on the lead octet + typedef typename std::iterator_traits::difference_type octet_difference_type; + const octet_difference_type length = utf8::internal::sequence_length(it); + + // Get trail octets and calculate the code point + utf_error err = UTF8_OK; + switch (length) { + case 0: + return INVALID_LEAD; + case 1: + err = utf8::internal::get_sequence_1(it, end, cp); + break; + case 2: + err = utf8::internal::get_sequence_2(it, end, cp); + break; + case 3: + err = utf8::internal::get_sequence_3(it, end, cp); + break; + case 4: + err = utf8::internal::get_sequence_4(it, end, cp); + break; + } + + if (err == UTF8_OK) { + // Decoding succeeded. Now, security checks... + if (utf8::internal::is_code_point_valid(cp)) { + if (!utf8::internal::is_overlong_sequence(cp, length)){ + // Passed! Return here. + code_point = cp; + ++it; + return UTF8_OK; + } + else + err = OVERLONG_SEQUENCE; + } + else + err = INVALID_CODE_POINT; + } + + // Failure branch - restore the original value of the iterator + it = original_it; + return err; + } + + template + inline utf_error validate_next(octet_iterator& it, octet_iterator end) { + uint32_t ignored; + return utf8::internal::validate_next(it, end, ignored); + } + +} // namespace internal + + /// The library API - functions intended to be called by the users + + // Byte order mark + const uint8_t bom[] = {0xef, 0xbb, 0xbf}; + + template + octet_iterator find_invalid(octet_iterator start, octet_iterator end) + { + octet_iterator result = start; + while (result != end) { + utf8::internal::utf_error err_code = utf8::internal::validate_next(result, end); + if (err_code != internal::UTF8_OK) + return result; + } + return result; + } + + template + inline bool is_valid(octet_iterator start, octet_iterator end) + { + return (utf8::find_invalid(start, end) == end); + } + + template + inline bool starts_with_bom (octet_iterator it, octet_iterator end) + { + return ( + ((it != end) && (utf8::internal::mask8(*it++)) == bom[0]) && + ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && + ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) + ); + } + + //Deprecated in release 2.3 + template + inline bool is_bom (octet_iterator it) + { + return ( + (utf8::internal::mask8(*it++)) == bom[0] && + (utf8::internal::mask8(*it++)) == bom[1] && + (utf8::internal::mask8(*it)) == bom[2] + ); + } +} // namespace utf8 + +#endif // header guard + + diff --git a/tests/test_string.cpp b/tests/test_string.cpp index d3661c24..20d96559 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -49,6 +49,7 @@ class TestString : public CppUnit::TestFixture CPPUNIT_TEST(testUpper); CPPUNIT_TEST(testEncodeNonLatin1); CPPUNIT_TEST(testEncodeEmpty); + CPPUNIT_TEST(testEncodeNonBMP); CPPUNIT_TEST(testIterator); CPPUNIT_TEST(testRedundantUTF8); CPPUNIT_TEST_SUITE_END(); @@ -313,6 +314,13 @@ public: CPPUNIT_ASSERT(empty.to8Bit(true).empty()); } + void testEncodeNonBMP() + { + const ByteVector a("\xFF\xFE\x3C\xD8\x50\xDD\x40\xD8\xF5\xDC\x3C\xD8\x00\xDE", 14); + const ByteVector b("\xF0\x9F\x85\x90\xF0\xA0\x83\xB5\xF0\x9F\x88\x80"); + CPPUNIT_ASSERT_EQUAL(b, String(a, String::UTF16).data(String::UTF8)); + } + void testIterator() { String s1 = "taglib string"; From 978b8227742a32dfd82a0aa886282b0cc1be2bd6 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 27 Jan 2017 15:11:08 +0900 Subject: [PATCH 22/37] Remove some redundant code in tstring.cpp. --- taglib/toolkit/tstring.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 1639d7c5..bee4fb7e 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -472,7 +472,7 @@ String String::upper() const static int shift = 'A' - 'a'; - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 'a' && *it <= 'z') s.d->data.push_back(*it + shift); else @@ -511,7 +511,7 @@ ByteVector String::data(Type t) const ByteVector v(size(), 0); char *p = v.data(); - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) + for(ConstIterator it = begin(); it != end(); ++it) *p++ = static_cast(*it); return v; @@ -540,7 +540,7 @@ ByteVector String::data(Type t) const *p++ = '\xff'; *p++ = '\xfe'; - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { *p++ = static_cast(*it & 0xff); *p++ = static_cast(*it >> 8); } @@ -552,7 +552,7 @@ ByteVector String::data(Type t) const ByteVector v(size() * 2, 0); char *p = v.data(); - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { *p++ = static_cast(*it >> 8); *p++ = static_cast(*it & 0xff); } @@ -564,7 +564,7 @@ ByteVector String::data(Type t) const ByteVector v(size() * 2, 0); char *p = v.data(); - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { *p++ = static_cast(*it & 0xff); *p++ = static_cast(*it >> 8); } @@ -614,7 +614,7 @@ String String::stripWhiteSpace() const bool String::isLatin1() const { - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 256) return false; } @@ -623,7 +623,7 @@ bool String::isLatin1() const bool String::isAscii() const { - for(wstring::const_iterator it = d->data.begin(); it != d->data.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 128) return false; } From 3d14ff74b1926693c9b18bbb7afa75f7461b8d2b Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Fri, 27 Jan 2017 22:10:02 +0900 Subject: [PATCH 23/37] Remove a duplicate test file. --- tests/data/vbri.mp3 | Bin 8192 -> 0 bytes tests/test_mpeg.cpp | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100644 tests/data/vbri.mp3 diff --git a/tests/data/vbri.mp3 b/tests/data/vbri.mp3 deleted file mode 100644 index ea14d61e90afb0b78b08fb7df8278c0502be27eb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8192 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 R37y4mlZ-?c?Y!cp{5KMtF0=pu diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index 5a990580..fd46f9ed 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -97,7 +97,7 @@ public: void testAudioPropertiesVBRIHeader() { - MPEG::File f(TEST_FILE_PATH_C("vbri.mp3")); + MPEG::File f(TEST_FILE_PATH_C("rare_frames.mp3")); CPPUNIT_ASSERT(f.audioProperties()); CPPUNIT_ASSERT_EQUAL(222, f.audioProperties()->length()); CPPUNIT_ASSERT_EQUAL(222, f.audioProperties()->lengthInSeconds()); From 922fd611ae20e4ba9423c2b46a70513979946a20 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 28 Jan 2017 01:17:21 +0900 Subject: [PATCH 24/37] Reduce useless memory reallocation in String::upper(). --- taglib/ape/apetag.cpp | 3 ++- taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp | 2 +- taglib/mpeg/id3v2/frames/urllinkframe.cpp | 2 +- taglib/toolkit/tstring.cpp | 5 ++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index 92ad536f..79e1d5cc 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -62,8 +62,9 @@ namespace return false; } + const String upperKey = String(key).upper(); for(size_t i = 0; invalidKeys[i] != 0; ++i) { - if(String(key).upper() == invalidKeys[i]) + if(upperKey == invalidKeys[i]) return false; } diff --git a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp index 87317d32..eafae171 100644 --- a/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp +++ b/taglib/mpeg/id3v2/frames/unsynchronizedlyricsframe.cpp @@ -118,7 +118,7 @@ PropertyMap UnsynchronizedLyricsFrame::asProperties() const { PropertyMap map; String key = description().upper(); - if(key.isEmpty() || key.upper() == "LYRICS") + if(key.isEmpty() || key == "LYRICS") map.insert("LYRICS", text()); else map.insert("LYRICS:" + key, text()); diff --git a/taglib/mpeg/id3v2/frames/urllinkframe.cpp b/taglib/mpeg/id3v2/frames/urllinkframe.cpp index 99e4fb7f..543b5184 100644 --- a/taglib/mpeg/id3v2/frames/urllinkframe.cpp +++ b/taglib/mpeg/id3v2/frames/urllinkframe.cpp @@ -170,7 +170,7 @@ PropertyMap UserUrlLinkFrame::asProperties() const { PropertyMap map; String key = description().upper(); - if(key.isEmpty() || key.upper() == "URL") + if(key.isEmpty() || key == "URL") map.insert("URL", url()); else map.insert("URL:" + key, url()); diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index bee4fb7e..863bbf59 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -469,12 +469,11 @@ String & String::clear() String String::upper() const { String s; - - static int shift = 'A' - 'a'; + s.d->data.reserve(size()); for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 'a' && *it <= 'z') - s.d->data.push_back(*it + shift); + s.d->data.push_back(*it + 'A' - 'a'); else s.d->data.push_back(*it); } From 598ab752bc682375340d8a13ee81da7ee496c960 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 30 Jan 2017 00:36:38 +0900 Subject: [PATCH 25/37] Stop assuming that std::wstring has a contiguous and null-terminated buffer. --- taglib/toolkit/tstring.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index b154cbae..4e9ae50d 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -23,8 +23,6 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -// This class assumes that std::basic_string has a contiguous and null-terminated buffer. - #include #include #include @@ -101,14 +99,12 @@ namespace } data.resize(length); - if(length > 0) { - if(swap) { - for(size_t i = 0; i < length; ++i) - data[i] = Utils::byteSwap(static_cast(s[i])); - } - else { - ::wmemcpy(&data[0], s, length); - } + for(size_t i = 0; i < length; ++i) { + unsigned short c = static_cast(s[i]); + if(swap) + c = Utils::byteSwap(c); + + data[i] = c; } } From 038b52ae01060089ee6d260e83df258725d8c1d4 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 30 Jan 2017 11:35:39 +0900 Subject: [PATCH 26/37] Check an invalid UTF-8 sequence consists of single char. Single char can be an invalid UTF sequence. For example, { 0x80 } is invalid. --- taglib/toolkit/tstring.cpp | 12 ++++++------ tests/test_string.cpp | 9 +++++++-- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 863bbf59..f02195c4 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -231,10 +231,6 @@ public: StringPrivate() : RefCounter() {} - StringPrivate(unsigned int n, wchar_t c) : - RefCounter(), - data(static_cast(n), c) {} - /*! * Stores string in UTF-16. The byte order depends on the CPU endian. */ @@ -334,9 +330,13 @@ String::String(wchar_t c, Type t) : } String::String(char c, Type t) : - d(new StringPrivate(1, static_cast(c))) + d(new StringPrivate()) { - if(t != Latin1 && t != UTF8) { + if(t == Latin1) + copyFromLatin1(d->data, &c, 1); + else if(t == String::UTF8) + copyFromUTF8(d->data, &c, 1); + else { debug("String::String() -- char should not contain UTF16."); } } diff --git a/tests/test_string.cpp b/tests/test_string.cpp index d3661c24..e460bfa1 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -50,7 +50,7 @@ class TestString : public CppUnit::TestFixture CPPUNIT_TEST(testEncodeNonLatin1); CPPUNIT_TEST(testEncodeEmpty); CPPUNIT_TEST(testIterator); - CPPUNIT_TEST(testRedundantUTF8); + CPPUNIT_TEST(testInvalidUTF8); CPPUNIT_TEST_SUITE_END(); public: @@ -331,12 +331,17 @@ public: CPPUNIT_ASSERT_EQUAL(L'I', *it2); } - void testRedundantUTF8() + void testInvalidUTF8() { CPPUNIT_ASSERT_EQUAL(String("/"), String(ByteVector("\x2F"), String::UTF8)); CPPUNIT_ASSERT(String(ByteVector("\xC0\xAF"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xE0\x80\xAF"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80\xAF"), String::UTF8).isEmpty()); + + CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80\x80"), String::UTF8).isEmpty()); + + CPPUNIT_ASSERT(String('\x80', String::UTF8).isEmpty()); } }; From 2c7ac6d6a9df1f6cead3a7b362e107490d7a4031 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 30 Jan 2017 12:56:53 +0900 Subject: [PATCH 27/37] Add a few more test cases for invalid UTF-8 sequences. --- tests/test_string.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 0ade0113..1bd29ff5 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -350,6 +350,9 @@ public: CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String('\x80', String::UTF8).isEmpty()); + + CPPUNIT_ASSERT(String(ByteVector("\xED\xA0\x80\xED\xB0\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty()); } }; From 6398796f95f9c3908379269e38dba9a5ed4d31cb Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 30 Jan 2017 16:11:59 +0900 Subject: [PATCH 28/37] Remove function bodies of some non-specialized template functions. The code won't link when a wrong version is used. It's better than showing a debug message. --- taglib/fileref.cpp | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/taglib/fileref.cpp b/taglib/fileref.cpp index 3a7f2c65..dca69f6a 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -62,11 +62,7 @@ namespace // Templatized internal functions. T should be String or IOStream*. template - FileName toFileName(T arg) - { - debug("FileRef::toFileName(): This version should never be called."); - return FileName(L""); - } + FileName toFileName(T arg); template <> FileName toFileName(IOStream *arg) @@ -82,11 +78,7 @@ namespace template File *resolveFileType(T arg, bool readProperties, - AudioProperties::ReadStyle style) - { - debug("FileRef::resolveFileType(): This version should never be called."); - return 0; - } + AudioProperties::ReadStyle style); template <> File *resolveFileType(IOStream *arg, bool readProperties, From 4552f2c2eb845971e117fd1af00f782e0b5f13be Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 30 Jan 2017 22:38:08 +0900 Subject: [PATCH 29/37] Remove redundant functions in tstring.cpp. Two versions of copyFromUTF16() are almost the same. --- taglib/toolkit/tstring.cpp | 100 +++++++++++++++---------------------- 1 file changed, 41 insertions(+), 59 deletions(-) diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index bc3861c1..283a7f2b 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -23,11 +23,8 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -#include #include #include -#include -#include #include #include @@ -76,22 +73,50 @@ namespace } } + // Helper functions to read a UTF-16 character from an array. + template + unsigned short nextUTF16(const T **p); + + template <> + unsigned short nextUTF16(const wchar_t **p) + { + return static_cast(*(*p)++); + } + + template <> + unsigned short nextUTF16(const char **p) + { + union { + unsigned short w; + char c[2]; + } u; + u.c[0] = *(*p)++; + u.c[1] = *(*p)++; + return u.w; + } + // Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into // UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. - void copyFromUTF16(std::wstring &data, const wchar_t *s, size_t length, String::Type t) + template + void copyFromUTF16(std::wstring &data, const T *s, size_t length, String::Type t) { bool swap; if(t == String::UTF16) { - if(length >= 1 && s[0] == 0xfeff) - swap = false; // Same as CPU endian. No need to swap bytes. - else if(length >= 1 && s[0] == 0xfffe) - swap = true; // Not same as CPU endian. Need to swap bytes. - else { - debug("String::copyFromUTF16() - Invalid UTF16 string."); + if(length < 1) { + debug("String::copyFromUTF16() - Invalid UTF16 string. Too short to have a BOM."); + return; + } + + const unsigned short bom = nextUTF16(&s); + if(bom == 0xfeff) + swap = false; // Same as CPU endian. No need to swap bytes. + else if(bom == 0xfffe) + swap = true; // Not same as CPU endian. Need to swap bytes. + else { + debug("String::copyFromUTF16() - Invalid UTF16 string. BOM is broken."); return; } - s++; length--; } else { @@ -100,54 +125,11 @@ namespace data.resize(length); for(size_t i = 0; i < length; ++i) { - unsigned short c = static_cast(s[i]); + const unsigned short c = nextUTF16(&s); if(swap) - c = Utils::byteSwap(c); - - data[i] = c; - } - } - - // Converts a UTF-16 (with BOM), UTF-16LE or UTF16-BE string into - // UTF-16(without BOM/CPU byte order) and copies it to the internal buffer. - void copyFromUTF16(std::wstring &data, const char *s, size_t length, String::Type t) - { - bool swap; - if(t == String::UTF16) { - if(length < 2) { - debug("String::copyFromUTF16() - Invalid UTF16 string."); - return; - } - - // Uses memcpy instead of reinterpret_cast to avoid an alignment exception. - unsigned short bom; - ::memcpy(&bom, s, 2); - - if(bom == 0xfeff) - swap = false; // Same as CPU endian. No need to swap bytes. - else if(bom == 0xfffe) - swap = true; // Not same as CPU endian. Need to swap bytes. - else { - debug("String::copyFromUTF16() - Invalid UTF16 string."); - return; - } - - s += 2; - length -= 2; - } - else { - swap = (t != wcharByteOrder()); - } - - data.resize(length / 2); - for(size_t i = 0; i < length / 2; ++i) { - unsigned short c; - ::memcpy(&c, s, 2); - if(swap) - c = Utils::byteSwap(c); - - data[i] = static_cast(c); - s += 2; + data[i] = Utils::byteSwap(c); + else + data[i] = c; } } } @@ -281,7 +263,7 @@ String::String(const ByteVector &v, Type t) : else if(t == UTF8) copyFromUTF8(d->data, v.data(), v.size()); else - copyFromUTF16(d->data, v.data(), v.size(), t); + copyFromUTF16(d->data, v.data(), v.size() / 2, t); // If we hit a null in the ByteVector, shrink the string again. d->data.resize(::wcslen(d->data.c_str())); From b74ffba4b568e16442fe79563c2e1312a69fa4d0 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 31 Jan 2017 00:21:41 +0900 Subject: [PATCH 30/37] Update NEWS. --- NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/NEWS b/NEWS index fd05c2cd..ac22cd93 100644 --- a/NEWS +++ b/NEWS @@ -4,10 +4,10 @@ * Added support for classical music tags of iTunes 12.5. * Dropped support for Windows 9x and NT 4.0 or older. * Fixed reading MP4 atoms with zero length. - * Fixed handling of redundant UTF-8 sequences in Win32. * Fixed handling of lowercase field names in Vorbis Comments. * Fixed possible file corruptions when saving Ogg files. * Fixed reading FLAC files with zero-sized seektables. + * Better handling of invalid UTF-8 sequences. * Several smaller bug fixes and performance improvements. TagLib 1.11.1 (Oct 24, 2016) From dd5ab2a08fc7b0120c9ff82bd945fd2bac2a059d Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 31 Jan 2017 14:19:30 +0900 Subject: [PATCH 31/37] Fix and add some test cases for invalid surrogate pairs. --- tests/test_string.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 1bd29ff5..abfbd8d2 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -178,9 +178,14 @@ public: const String s2(v2, String::UTF8); CPPUNIT_ASSERT_EQUAL(s2.data(String::UTF16), v1); - const ByteVector v3("\xff\xfe\xb7\xdf\xce\x91\x4b\x5c"); - const String s3(v2, String::UTF16); - CPPUNIT_ASSERT(s3.isEmpty()); + const ByteVector v3("\xfe\xff\xd8\x01\x30\x42"); + CPPUNIT_ASSERT(String(v3, String::UTF16).data(String::UTF8).isEmpty()); + + const ByteVector v4("\xfe\xff\x30\x42\xdc\x01"); + CPPUNIT_ASSERT(String(v4, String::UTF16).data(String::UTF8).isEmpty()); + + const ByteVector v5("\xfe\xff\xdc\x01\xd8\x01"); + CPPUNIT_ASSERT(String(v5, String::UTF16).data(String::UTF8).isEmpty()); } void testAppendStringDetach() From 87fc4012f47f353bc38f96bca8299e8178bf84a6 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Tue, 31 Jan 2017 14:27:23 +0900 Subject: [PATCH 32/37] Add some test cases for invalid UTF-8 sequences. --- tests/test_string.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/test_string.cpp b/tests/test_string.cpp index abfbd8d2..6f00b62d 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -354,6 +354,12 @@ public: CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80\x80"), String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xC2"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xE0\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xF0\x80\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xF8\x80\x80\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String(ByteVector("\xFC\x80\x80\x80\x80"), String::UTF8).isEmpty()); + CPPUNIT_ASSERT(String('\x80', String::UTF8).isEmpty()); CPPUNIT_ASSERT(String(ByteVector("\xED\xA0\x80\xED\xB0\x80"), String::UTF8).isEmpty()); From ba98628919c97a00e8e597bdebd6af3e9210f643 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 1 Feb 2017 14:23:03 +0900 Subject: [PATCH 33/37] Avoid searching the same area twice in MPEG::File::previousFrameOffset(). --- taglib/mpeg/mpegfile.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/taglib/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index b8ff40dd..74bf779b 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -373,15 +373,13 @@ long MPEG::File::previousFrameOffset(long position) ByteVector frameSyncBytes(2, '\0'); while(position > 0) { - const long size = std::min(position, bufferSize()); - position -= size; + const long bufferLength = std::min(position, bufferSize()); + position -= bufferLength; seek(position); - const ByteVector buffer = readBlock(bufferSize()); - if(buffer.isEmpty()) - return -1; + const ByteVector buffer = readBlock(bufferLength); - for(int i = buffer.size() - 1; i >= 0; i--) { + for(int i = buffer.size() - 1; i >= 0; --i) { frameSyncBytes[1] = frameSyncBytes[0]; frameSyncBytes[0] = buffer[i]; if(isFrameSync(frameSyncBytes)) { From 179c175a6cdd7969538fdf7f19b97dcdc7ac9dc9 Mon Sep 17 00:00:00 2001 From: Scott Wheeler Date: Fri, 3 Feb 2017 13:56:02 +0000 Subject: [PATCH 34/37] Ignore warnings about OSAtomicIncrement32Barrier The warnings suggest moving to std::atomic functions, but those are only available in C++11. It would be possible to switch to the C versions of those functions, which are now provided in stdatoic.h (in C11), but let's wait until we actually hit problems with this function and are a few more OS versions into C11 headers being included by default. --- taglib/toolkit/trefcounter.h | 1 + 1 file changed, 1 insertion(+) diff --git a/taglib/toolkit/trefcounter.h b/taglib/toolkit/trefcounter.h index bc3cdd9b..db97c538 100644 --- a/taglib/toolkit/trefcounter.h +++ b/taglib/toolkit/trefcounter.h @@ -30,6 +30,7 @@ #include "taglib.h" #ifdef __APPLE__ +# define OSATOMIC_DEPRECATED 0 # include # define TAGLIB_ATOMIC_MAC #elif defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__) From a5d9e49c4958ef4e91785b74afab52964a521499 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Sat, 4 Feb 2017 01:31:20 +0900 Subject: [PATCH 35/37] Remove obsolete comments. The bug mentioned in the comments are already fixed. --- taglib/ogg/flac/oggflacfile.h | 3 --- taglib/ogg/opus/opusfile.h | 3 --- taglib/ogg/speex/speexfile.h | 3 --- taglib/ogg/vorbis/vorbisfile.h | 3 --- 4 files changed, 12 deletions(-) diff --git a/taglib/ogg/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h index 28b3f67f..05762f9b 100644 --- a/taglib/ogg/flac/oggflacfile.h +++ b/taglib/ogg/flac/oggflacfile.h @@ -127,9 +127,6 @@ namespace TagLib { /*! * Save the file. This will primarily save and update the XiphComment. * Returns true if the save is successful. - * - * \warning In the current implementation, it's dangerous to call save() - * repeatedly. It leads to a segfault. */ virtual bool save(); diff --git a/taglib/ogg/opus/opusfile.h b/taglib/ogg/opus/opusfile.h index 0363b584..b718f0d7 100644 --- a/taglib/ogg/opus/opusfile.h +++ b/taglib/ogg/opus/opusfile.h @@ -110,9 +110,6 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. - * - * \warning In the current implementation, it's dangerous to call save() - * repeatedly. It leads to a segfault. */ virtual bool save(); diff --git a/taglib/ogg/speex/speexfile.h b/taglib/ogg/speex/speexfile.h index de38bfbf..58b001dd 100644 --- a/taglib/ogg/speex/speexfile.h +++ b/taglib/ogg/speex/speexfile.h @@ -110,9 +110,6 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. - * - * \warning In the current implementation, it's dangerous to call save() - * repeatedly. It leads to a segfault. */ virtual bool save(); diff --git a/taglib/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h index 48d9d7ca..9e71dcbe 100644 --- a/taglib/ogg/vorbis/vorbisfile.h +++ b/taglib/ogg/vorbis/vorbisfile.h @@ -118,9 +118,6 @@ namespace TagLib { * Save the file. * * This returns true if the save was successful. - * - * \warning In the current implementation, it's dangerous to call save() - * repeatedly. It leads to a segfault. */ virtual bool save(); From dd4adf94ced99142dcddd7a943b7742a979ec402 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Mon, 6 Feb 2017 10:06:10 +0900 Subject: [PATCH 36/37] Fix wrong endian of boolean values when saving ASF files. --- taglib/asf/asfattribute.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/taglib/asf/asfattribute.cpp b/taglib/asf/asfattribute.cpp index ec909fe8..6faf7973 100644 --- a/taglib/asf/asfattribute.cpp +++ b/taglib/asf/asfattribute.cpp @@ -281,10 +281,10 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const case BoolType: if(kind == 0) { - data.append(ByteVector::fromUInt(toBool())); + data.append(ByteVector::fromUInt(toBool(), false)); } else { - data.append(ByteVector::fromShort(toBool())); + data.append(ByteVector::fromShort(toBool(), false)); } break; From 45ee18e206bea7bf6ea45da892211ec89e4f1b1d Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Wed, 8 Feb 2017 17:45:46 +0900 Subject: [PATCH 37/37] FilePrivate is responsible to delete a stream pointer instead of File. Generally, TagLib leaves the Private classes to delete their members. --- taglib/toolkit/tfile.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/taglib/toolkit/tfile.cpp b/taglib/toolkit/tfile.cpp index 897400b8..aff1684d 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -69,20 +69,22 @@ using namespace TagLib; class File::FilePrivate { public: - FilePrivate(IOStream *stream, bool owner); + FilePrivate(IOStream *stream, bool owner) : + stream(stream), + streamOwner(owner), + valid(true) {} + + ~FilePrivate() + { + if(streamOwner) + delete stream; + } IOStream *stream; bool streamOwner; bool valid; }; -File::FilePrivate::FilePrivate(IOStream *stream, bool owner) : - stream(stream), - streamOwner(owner), - valid(true) -{ -} - //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// @@ -99,8 +101,6 @@ File::File(IOStream *stream) : File::~File() { - if(d->stream && d->streamOwner) - delete d->stream; delete d; }