diff --git a/.travis.yml b/.travis.yml index d724ae9d..b39de3c5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,6 +15,7 @@ addons: packages: - libcppunit-dev - zlib1g-dev + - libboost-dev matrix: exclude: diff --git a/ConfigureChecks.cmake b/ConfigureChecks.cmake index e1f97027..cd6e66ec 100644 --- a/ConfigureChecks.cmake +++ b/ConfigureChecks.cmake @@ -57,7 +57,7 @@ endif() check_cxx_source_compiles(" #include int main() { - std::atomic_int x; + std::atomic_int x(1); ++x; --x; return 0; @@ -137,69 +137,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/NEWS b/NEWS index 774e6532..ac22cd93 100644 --- a/NEWS +++ b/NEWS @@ -4,9 +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) diff --git a/config.h.cmake b/config.h.cmake index 4c227b3e..56b0ec4e 100644 --- a/config.h.cmake +++ b/config.h.cmake @@ -9,7 +9,6 @@ #cmakedefine _FILE_OFFSET_BITS ${_FILE_OFFSET_BITS} /* 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/CMakeLists.txt b/taglib/CMakeLists.txt index 92efd64c..613045ad 100644 --- a/taglib/CMakeLists.txt +++ b/taglib/CMakeLists.txt @@ -344,12 +344,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 @@ -367,7 +361,7 @@ set(tag_LIB_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} ${ebml_SRCS} ${matroska_SRCS} ${dsf_SRCS} - ${unicode_SRCS} ${zlib_SRCS} + ${zlib_SRCS} tag.cpp tagunion.cpp fileref.cpp diff --git a/taglib/ape/apetag.cpp b/taglib/ape/apetag.cpp index 334f57d7..bd15bac0 100644 --- a/taglib/ape/apetag.cpp +++ b/taglib/ape/apetag.cpp @@ -63,8 +63,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/asf/asfattribute.cpp b/taglib/asf/asfattribute.cpp index e283b3ea..6cd98e63 100644 --- a/taglib/asf/asfattribute.cpp +++ b/taglib/asf/asfattribute.cpp @@ -1,4 +1,4 @@ -/************************************************************************** +/************************************************************************** copyright : (C) 2005-2007 by Lukáš Lalinský email : lalinsky@gmail.com **************************************************************************/ @@ -37,16 +37,16 @@ namespace { struct AttributeData { + AttributeData() : + numericValue(0), + stream(0), + language(0) {} + ASF::Attribute::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; }; @@ -59,8 +59,6 @@ public: data(new AttributeData()) { data->pictureValue = ASF::Picture::fromInvalid(); - data->stream = 0; - data->language = 0; } SHARED_PTR data; @@ -106,33 +104,28 @@ ASF::Attribute::Attribute(unsigned int value) : d(new AttributePrivate()) { d->data->type = DWordType; - d->data->intValue = value; + d->data->numericValue = value; } ASF::Attribute::Attribute(unsigned long long value) : d(new AttributePrivate()) { d->data->type = QWordType; - d->data->longLongValue = value; + d->data->numericValue = value; } ASF::Attribute::Attribute(unsigned short value) : d(new AttributePrivate()) { d->data->type = WordType; - d->data->shortValue = value; + d->data->numericValue = value; } ASF::Attribute::Attribute(bool value) : d(new AttributePrivate()) { d->data->type = BoolType; - d->data->boolValue = value; -} - -ASF::Attribute::~Attribute() -{ - delete d; + d->data->numericValue = value; } ASF::Attribute &ASF::Attribute::operator=(const ASF::Attribute &other) @@ -148,6 +141,11 @@ void ASF::Attribute::swap(Attribute &other) swap(d, other.d); } +ASF::Attribute::~Attribute() +{ + delete d; +} + ASF::Attribute::AttributeTypes ASF::Attribute::type() const { return d->data->type; @@ -168,22 +166,22 @@ ByteVector ASF::Attribute::toByteVector() const unsigned short ASF::Attribute::toBool() const { - return d->data->shortValue; + return d->data->numericValue ? 1 : 0; } unsigned short ASF::Attribute::toUShort() const { - return d->data->shortValue; + return static_cast(d->data->numericValue); } unsigned int ASF::Attribute::toUInt() const { - return d->data->intValue; + return static_cast(d->data->numericValue); } unsigned long long ASF::Attribute::toULongLong() const { - return d->data->longLongValue; + return static_cast(d->data->numericValue); } ASF::Picture ASF::Attribute::toPicture() const @@ -223,24 +221,24 @@ String ASF::Attribute::parse(ASF::File &f, int kind) switch(d->data->type) { case WordType: - d->data->shortValue = readWORD(&f); + d->data->numericValue = readWORD(&f); break; case BoolType: if(kind == 0) { - d->data->boolValue = (readDWORD(&f) == 1); + d->data->numericValue = (readDWORD(&f) != 0); } else { - d->data->boolValue = (readWORD(&f) == 1); + d->data->numericValue = (readWORD(&f) != 0); } break; case DWordType: - d->data->intValue = readDWORD(&f); + d->data->numericValue = readDWORD(&f); break; case QWordType: - d->data->longLongValue = readQWORD(&f); + d->data->numericValue = readQWORD(&f); break; case UnicodeType: @@ -291,24 +289,24 @@ ByteVector ASF::Attribute::render(const String &name, int kind) const switch (d->data->type) { case WordType: - data.append(ByteVector::fromUInt16LE(d->data->shortValue)); + data.append(ByteVector::fromUInt16LE(toUShort())); break; case BoolType: if(kind == 0) { - data.append(ByteVector::fromUInt32LE(d->data->boolValue ? 1 : 0)); + data.append(ByteVector::fromUInt32LE(toBool())); } else { - data.append(ByteVector::fromUInt16LE(d->data->boolValue ? 1 : 0)); + data.append(ByteVector::fromUInt16LE(toBool())); } break; case DWordType: - data.append(ByteVector::fromUInt32LE(d->data->intValue)); + data.append(ByteVector::fromUInt32LE(toUInt())); break; case QWordType: - data.append(ByteVector::fromUInt64LE(d->data->longLongValue)); + data.append(ByteVector::fromUInt64LE(toULongLong())); break; case UnicodeType: diff --git a/taglib/asf/asftag.cpp b/taglib/asf/asftag.cpp index e346bdc4..28170180 100644 --- a/taglib/asf/asftag.cpp +++ b/taglib/asf/asftag.cpp @@ -40,10 +40,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/fileref.cpp b/taglib/fileref.cpp index 676ec4f2..87452351 100644 --- a/taglib/fileref.cpp +++ b/taglib/fileref.cpp @@ -63,11 +63,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) @@ -83,11 +79,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, diff --git a/taglib/flac/flacfile.cpp b/taglib/flac/flacfile.cpp index ac4ce6f8..cd7d889e 100644 --- a/taglib/flac/flacfile.cpp +++ b/taglib/flac/flacfile.cpp @@ -183,7 +183,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 long originalLength = d->streamStart - d->flacStart; long long paddingLength = originalLength - data.size() - 4; @@ -476,7 +475,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/taglib/flac/flacpicture.cpp b/taglib/flac/flacpicture.cpp index de7bd4c4..5c4fd3e0 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 82840e8a..7f20e7f3 100644 --- a/taglib/it/itproperties.cpp +++ b/taglib/it/itproperties.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright :(C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ diff --git a/taglib/mod/modproperties.cpp b/taglib/mod/modproperties.cpp index 8acfe5b8..23348f87 100644 --- a/taglib/mod/modproperties.cpp +++ b/taglib/mod/modproperties.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ @@ -48,7 +48,7 @@ public: Mod::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : TagLib::AudioProperties(), - d(new PropertiesPrivate) + d(new PropertiesPrivate()) { } diff --git a/taglib/mod/modtag.cpp b/taglib/mod/modtag.cpp index 4ffbc194..5d50cab3 100644 --- a/taglib/mod/modtag.cpp +++ b/taglib/mod/modtag.cpp @@ -44,9 +44,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 89e2de5d..0b546aa3 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 79a13ae6..c8d453dc 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. @@ -303,9 +302,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..815e5e1a 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); } @@ -188,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 17d2f0c1..b21e2739 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); } @@ -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/generalencapsulatedobjectframe.cpp b/taglib/mpeg/id3v2/frames/generalencapsulatedobjectframe.cpp index ac711181..0c645435 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 15ef6ade..5fcb4b19 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 c0d6e661..7b94b941 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); } @@ -131,8 +133,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 e70f831a..ce993ad3 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); } @@ -120,8 +122,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/relativevolumeframe.cpp b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp index 01acebae..2d40c6f7 100644 --- a/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp +++ b/taglib/mpeg/id3v2/frames/relativevolumeframe.cpp @@ -54,14 +54,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); } @@ -196,8 +198,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 bbd7092a..6549b9d3 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); } @@ -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/tableofcontentsframe.cpp b/taglib/mpeg/id3v2/frames/tableofcontentsframe.cpp index 38c95bbe..12514337 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 21c3bc2a..7eb413d5 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 eef4e51e..ddece76d 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..eafae171 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); } @@ -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()); @@ -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/frames/urllinkframe.cpp b/taglib/mpeg/id3v2/frames/urllinkframe.cpp index dfd8013d..5fa643e2 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); } @@ -158,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()); @@ -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/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/mpeg/id3v2/id3v2frame.cpp b/taglib/mpeg/id3v2/id3v2frame.cpp index 5edf569f..7a522337 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; } @@ -567,15 +567,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 e59ba0fd..219c26af 100644 --- a/taglib/mpeg/id3v2/id3v2tag.cpp +++ b/taglib/mpeg/id3v2/id3v2tag.cpp @@ -903,7 +903,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/mpeg/mpegfile.cpp b/taglib/mpeg/mpegfile.cpp index b3f925cb..b9a4b50f 100644 --- a/taglib/mpeg/mpegfile.cpp +++ b/taglib/mpeg/mpegfile.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ @@ -321,55 +321,50 @@ void MPEG::File::setID3v2FrameFactory(const ID3v2::FrameFactory *factory) long long MPEG::File::nextFrameOffset(long long position) { - bool foundLastSyncPattern = false; - - ByteVector buffer; + ByteVector frameSyncBytes(2, '\0'); while(true) { seek(position); - buffer = readBlock(bufferSize()); - - if(buffer.size() <= 0) + const ByteVector buffer = readBlock(bufferSize()); + if(buffer.isEmpty()) 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(size_t i = 0; i < buffer.size(); ++i) { + frameSyncBytes[0] = frameSyncBytes[1]; + frameSyncBytes[1] = buffer[i]; + if(isFrameSync(frameSyncBytes)) { + const 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 long MPEG::File::previousFrameOffset(long long position) { - bool foundFirstSyncPattern = false; - ByteVector buffer; + ByteVector frameSyncBytes(2, '\0'); while(position > 0) { - size_t size = static_cast(std::min(position, bufferSize())); - position -= size; + const long long bufferLength = std::min(position, bufferSize()); + position -= bufferLength; seek(position); - buffer = readBlock(size); + const ByteVector buffer = readBlock(static_cast(bufferLength)); - if(buffer.isEmpty()) - break; - - if(foundFirstSyncPattern && firstSyncByte(buffer[buffer.size() - 1])) - return position + buffer.size() - 1; - - for(int i = static_cast(buffer.size()) - 2; i >= 0; i--) { - if(firstSyncByte(buffer[i]) && secondSynchByte(buffer[i + 1])) - return position + i; + for(int i = static_cast(buffer.size()) - 1; i >= 0; --i) { + frameSyncBytes[1] = frameSyncBytes[0]; + frameSyncBytes[0] = buffer[i]; + if(isFrameSync(frameSyncBytes)) { + const Header header(this, position + i, true); + if(header.isValid()) + return position + i + header.frameLength(); + } } - - foundFirstSyncPattern = secondSynchByte(buffer[0]); } + return -1; } @@ -463,28 +458,41 @@ long 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])) + const 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 long tagOffset = find(headerID); - if(tagOffset < 0) - return -1; + ByteVector frameSyncBytes(2, '\0'); + ByteVector tagHeaderBytes(3, '\0'); + long long position = 0; - const long 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(size_t i = 0; i < buffer.size(); ++i) { + frameSyncBytes[0] = frameSyncBytes[1]; + frameSyncBytes[1] = buffer[i]; + if(isFrameSync(frameSyncBytes)) { + const 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(tagHeaderBytes == headerID) + return position + i - 2; + } + + position += bufferSize(); + } } diff --git a/taglib/mpeg/mpegheader.cpp b/taglib/mpeg/mpegheader.cpp index d2e528c7..cd54f67c 100644 --- a/taglib/mpeg/mpegheader.cpp +++ b/taglib/mpeg/mpegheader.cpp @@ -182,7 +182,7 @@ void MPEG::Header::parse(File *file, long 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/mpegproperties.cpp b/taglib/mpeg/mpegproperties.cpp index 1409ea19..a4d29d34 100644 --- a/taglib/mpeg/mpegproperties.cpp +++ b/taglib/mpeg/mpegproperties.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ @@ -152,23 +152,13 @@ void MPEG::AudioProperties::read(File *file) { // Only the first valid frame is required if we have a VBR header. - long long firstFrameOffset = file->firstFrameOffset(); + const long 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. @@ -200,24 +190,13 @@ void MPEG::AudioProperties::read(File *file) // Look for the last MPEG audio frame to calculate the stream length. - long long lastFrameOffset = file->lastFrameOffset(); + const long 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 long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength(); if(streamLength > 0) d->length = static_cast(streamLength * 8.0 / d->bitrate + 0.5); 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); } } diff --git a/taglib/ogg/flac/oggflacfile.cpp b/taglib/ogg/flac/oggflacfile.cpp index 9a4cfef9..b522144e 100644 --- a/taglib/ogg/flac/oggflacfile.cpp +++ b/taglib/ogg/flac/oggflacfile.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2004-2005 by Allan Sandfeld Jensen email : kde@carewolf.org ***************************************************************************/ @@ -70,17 +70,19 @@ public: //////////////////////////////////////////////////////////////////////////////// Ogg::FLAC::File::File(FileName file, bool readProperties, - AudioProperties::ReadStyle propertiesStyle) : Ogg::File(file) + AudioProperties::ReadStyle propertiesStyle) : + Ogg::File(file), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) read(readProperties, propertiesStyle); } Ogg::FLAC::File::File(IOStream *stream, bool readProperties, - AudioProperties::ReadStyle propertiesStyle) : Ogg::File(stream) + AudioProperties::ReadStyle propertiesStyle) : + Ogg::File(stream), + d(new FilePrivate()) { - d = new FilePrivate; if(isOpen()) read(readProperties, propertiesStyle); } @@ -161,7 +163,7 @@ void Ogg::FLAC::File::read(bool readProperties, AudioProperties::ReadStyle prope 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/flac/oggflacfile.h b/taglib/ogg/flac/oggflacfile.h index 4c2d7956..8ae180aa 100644 --- a/taglib/ogg/flac/oggflacfile.h +++ b/taglib/ogg/flac/oggflacfile.h @@ -113,9 +113,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.cpp b/taglib/ogg/opus/opusfile.cpp index 554d3bf7..e7f87dd9 100644 --- a/taglib/ogg/opus/opusfile.cpp +++ b/taglib/ogg/opus/opusfile.cpp @@ -91,7 +91,7 @@ Opus::AudioProperties *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/opus/opusfile.h b/taglib/ogg/opus/opusfile.h index 4384fea5..b80d4dc0 100644 --- a/taglib/ogg/opus/opusfile.h +++ b/taglib/ogg/opus/opusfile.h @@ -98,9 +98,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.cpp b/taglib/ogg/speex/speexfile.cpp index b0572282..5a0cf07d 100644 --- a/taglib/ogg/speex/speexfile.cpp +++ b/taglib/ogg/speex/speexfile.cpp @@ -91,7 +91,7 @@ Speex::AudioProperties *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/speex/speexfile.h b/taglib/ogg/speex/speexfile.h index 193e5d6f..3238008d 100644 --- a/taglib/ogg/speex/speexfile.h +++ b/taglib/ogg/speex/speexfile.h @@ -98,9 +98,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.cpp b/taglib/ogg/vorbis/vorbisfile.cpp index d1b822dd..808de0a1 100644 --- a/taglib/ogg/vorbis/vorbisfile.cpp +++ b/taglib/ogg/vorbis/vorbisfile.cpp @@ -99,7 +99,7 @@ bool Ogg::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/ogg/vorbis/vorbisfile.h b/taglib/ogg/vorbis/vorbisfile.h index cd130258..f975f5e5 100644 --- a/taglib/ogg/vorbis/vorbisfile.h +++ b/taglib/ogg/vorbis/vorbisfile.h @@ -96,9 +96,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/riff/rifffile.cpp b/taglib/riff/rifffile.cpp index 158e3cf9..f63eef9d 100644 --- a/taglib/riff/rifffile.cpp +++ b/taglib/riff/rifffile.cpp @@ -206,7 +206,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; } diff --git a/taglib/s3m/s3mproperties.cpp b/taglib/s3m/s3mproperties.cpp index 03b6dd03..05ae6179 100644 --- a/taglib/s3m/s3mproperties.cpp +++ b/taglib/s3m/s3mproperties.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ @@ -60,10 +60,6 @@ public: unsigned char bpmSpeed; }; -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - S3M::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : TagLib::AudioProperties(), d(new PropertiesPrivate()) diff --git a/taglib/tagunion.cpp b/taglib/tagunion.cpp index e94125ea..6480dcba 100644 --- a/taglib/tagunion.cpp +++ b/taglib/tagunion.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ diff --git a/taglib/toolkit/tbytevectorstream.cpp b/taglib/toolkit/tbytevectorstream.cpp index eaf23a8f..231db989 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 8966a37c..28e66e02 100644 --- a/taglib/toolkit/tfile.cpp +++ b/taglib/toolkit/tfile.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ @@ -33,53 +33,25 @@ using namespace TagLib; -class File::FilePrivateBase +class File::FilePrivate { public: - FilePrivateBase() : + FilePrivate(IOStream *stream, bool owner) : + stream(stream), + streamOwner(owner), valid(true) {} - virtual ~FilePrivateBase() {} - - virtual IOStream *stream() const = 0; + ~FilePrivate() + { + if(streamOwner) + delete stream; + } + IOStream *stream; + bool streamOwner; bool valid; }; -// FilePrivate implementation which takes ownership of the stream. - -class File::ManagedFilePrivate : public File::FilePrivateBase -{ -public: - ManagedFilePrivate(IOStream *stream) : - p(stream) {} - - virtual IOStream *stream() const - { - return p.get(); - } - -private: - SCOPED_PTR p; -}; - -// FilePrivate implementation which doesn't take ownership of the stream. - -class File::UnmanagedFilePrivate : public File::FilePrivateBase -{ -public: - UnmanagedFilePrivate(IOStream *stream) : - p(stream) {} - - virtual IOStream *stream() const - { - return p; - } - -private: - IOStream *p; -}; - //////////////////////////////////////////////////////////////////////////////// // public members //////////////////////////////////////////////////////////////////////////////// @@ -91,7 +63,7 @@ File::~File() FileName File::name() const { - return d->stream()->name(); + return d->stream->name(); } PropertyMap File::properties() const @@ -111,17 +83,17 @@ PropertyMap File::setProperties(const PropertyMap &properties) ByteVector File::readBlock(size_t length) { - return d->stream()->readBlock(length); + return d->stream->readBlock(length); } void File::writeBlock(const ByteVector &data) { - d->stream()->writeBlock(data); + d->stream->writeBlock(data); } long long File::find(const ByteVector &pattern, long long fromOffset, const ByteVector &before) { - if(!d->stream() || pattern.size() > bufferSize()) + if(!d->stream || pattern.size() > bufferSize()) return -1; // The position in the file that the current buffer starts at. @@ -225,7 +197,7 @@ long long File::find(const ByteVector &pattern, long long fromOffset, const Byte long long File::rfind(const ByteVector &pattern, long long fromOffset, const ByteVector &before) { - if(!d->stream() || pattern.size() > bufferSize()) + if(!d->stream || pattern.size() > bufferSize()) return -1; // Save the location of the current read pointer. We will restore the @@ -287,22 +259,22 @@ long long File::rfind(const ByteVector &pattern, long long fromOffset, const Byt void File::insert(const ByteVector &data, long long start, size_t replace) { - d->stream()->insert(data, start, replace); + d->stream->insert(data, start, replace); } void File::removeBlock(long long start, size_t length) { - d->stream()->removeBlock(start, length); + d->stream->removeBlock(start, length); } bool File::readOnly() const { - return d->stream()->readOnly(); + return d->stream->readOnly(); } bool File::isOpen() const { - return d->stream()->isOpen(); + return d->stream->isOpen(); } bool File::isValid() const @@ -312,27 +284,27 @@ bool File::isValid() const void File::seek(long long offset, Position p) { - d->stream()->seek(offset, IOStream::Position(p)); + d->stream->seek(offset, IOStream::Position(p)); } void File::truncate(long long length) { - d->stream()->truncate(length); + d->stream->truncate(length); } void File::clear() { - d->stream()->clear(); + d->stream->clear(); } long long File::tell() const { - return d->stream()->tell(); + return d->stream->tell(); } long long File::length() { - return d->stream()->length(); + return d->stream->length(); } String File::toString() const @@ -353,13 +325,13 @@ String File::toString() const // protected members //////////////////////////////////////////////////////////////////////////////// -File::File(const FileName &fileName) - : d(new ManagedFilePrivate(new FileStream(fileName))) +File::File(const FileName &fileName) : + d(new FilePrivate(new FileStream(fileName), true)) { } -File::File(IOStream *stream) - : d(new UnmanagedFilePrivate(stream)) +File::File(IOStream *stream) : + d(new FilePrivate(stream, false)) { } diff --git a/taglib/toolkit/tfile.h b/taglib/toolkit/tfile.h index 9a3f2e1f..fea888a6 100644 --- a/taglib/toolkit/tfile.h +++ b/taglib/toolkit/tfile.h @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ @@ -282,10 +282,8 @@ namespace TagLib { File(const File &); File &operator=(const File &); - class FilePrivateBase; - class ManagedFilePrivate; - class UnmanagedFilePrivate; - FilePrivateBase *d; + class FilePrivate; + FilePrivate *d; }; } diff --git a/taglib/toolkit/trefcounter.cpp b/taglib/toolkit/trefcounter.cpp index c8882f1a..228fc4de 100644 --- a/taglib/toolkit/trefcounter.cpp +++ b/taglib/toolkit/trefcounter.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ diff --git a/taglib/toolkit/trefcounter.h b/taglib/toolkit/trefcounter.h index 260fa948..b6d2d0cf 100644 --- a/taglib/toolkit/trefcounter.h +++ b/taglib/toolkit/trefcounter.h @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2013 by Tsuda Kageyu email : tsuda.kageyu@gmail.com ***************************************************************************/ diff --git a/taglib/toolkit/tstring.cpp b/taglib/toolkit/tstring.cpp index 3f849e8f..ff2db588 100644 --- a/taglib/toolkit/tstring.cpp +++ b/taglib/toolkit/tstring.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2002 - 2008 by Scott Wheeler email : wheeler@kde.org ***************************************************************************/ @@ -23,24 +23,14 @@ * http://www.mozilla.org/MPL/ * ***************************************************************************/ -// This class assumes that std::basic_string has a contiguous and null-terminated buffer. - -#include -#include -#include #include #include -#ifdef _WIN32 -# include -#else -# include "unicode.h" -#endif - #include #include #include #include +#include #include "tstring.h" @@ -48,72 +38,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,28 +63,60 @@ 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(); + } + } + + // 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 { @@ -168,57 +124,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); - } - } - } - - // 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); + for(size_t i = 0; i < length; ++i) { + const unsigned short c = nextUTF16(&s); if(swap) - c = Utils::byteSwap(c); - - data[i] = static_cast(c); - s += 2; + data[i] = Utils::byteSwap(c); + else + data[i] = c; } } } @@ -354,7 +265,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())); @@ -469,12 +380,14 @@ String & String::clear() String String::upper() const { - static const int shift = 'A' - 'a'; + String s; + s.d->data->reserve(size()); - String s(*d->data); - for(Iterator it = s.begin(); it != s.end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 'a' && *it <= 'z') - *it = *it + shift; + s.d->data->push_back(*it + 'A' - 'a'); + else + s.d->data->push_back(*it); } return s; @@ -504,24 +417,26 @@ ByteVector String::data(Type t) const ByteVector v(size(), 0); char *p = v.data(); - for(std::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; } 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(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); @@ -532,7 +447,7 @@ ByteVector String::data(Type t) const *p++ = '\xff'; *p++ = '\xfe'; - for(std::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); } @@ -544,7 +459,7 @@ ByteVector String::data(Type t) const ByteVector v(size() * 2, 0); char *p = v.data(); - for(std::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); } @@ -556,7 +471,7 @@ ByteVector String::data(Type t) const ByteVector v(size() * 2, 0); char *p = v.data(); - for(std::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); } @@ -601,7 +516,7 @@ String String::stripWhiteSpace() const bool String::isLatin1() const { - for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 256) return false; } @@ -610,7 +525,7 @@ bool String::isLatin1() const bool String::isAscii() const { - for(std::wstring::const_iterator it = d->data->begin(); it != d->data->end(); ++it) { + for(ConstIterator it = begin(); it != end(); ++it) { if(*it >= 128) return false; } diff --git a/taglib/toolkit/tutils.h b/taglib/toolkit/tutils.h index 6d1fa5fc..9ecd9dea 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); 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/taglib/xm/xmproperties.cpp b/taglib/xm/xmproperties.cpp index c053a3e0..b0b7b88a 100644 --- a/taglib/xm/xmproperties.cpp +++ b/taglib/xm/xmproperties.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2011 by Mathias Panzenböck email : grosser.meister.morti@gmx.net ***************************************************************************/ @@ -56,10 +56,6 @@ public: unsigned short bpmSpeed; }; -//////////////////////////////////////////////////////////////////////////////// -// public members -//////////////////////////////////////////////////////////////////////////////// - XM::AudioProperties::AudioProperties(AudioProperties::ReadStyle) : TagLib::AudioProperties(), d(new PropertiesPrivate()) diff --git a/tests/data/empty-seektable.flac b/tests/data/empty-seektable.flac new file mode 100644 index 00000000..20dd90d9 Binary files /dev/null and b/tests/data/empty-seektable.flac differ diff --git a/tests/data/garbage.mp3 b/tests/data/garbage.mp3 new file mode 100644 index 00000000..730b74e7 Binary files /dev/null and b/tests/data/garbage.mp3 differ 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/data/vbri.mp3 b/tests/data/vbri.mp3 deleted file mode 100644 index ea14d61e..00000000 Binary files a/tests/data/vbri.mp3 and /dev/null differ 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()); } diff --git a/tests/test_flac.cpp b/tests/test_flac.cpp index 061cbcd0..0a51c2f9 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); diff --git a/tests/test_mpeg.cpp b/tests/test_mpeg.cpp index adfc5986..de4c9d3c 100644 --- a/tests/test_mpeg.cpp +++ b/tests/test_mpeg.cpp @@ -1,4 +1,4 @@ -/*************************************************************************** +/*************************************************************************** copyright : (C) 2007 by Lukas Lalinsky email : lukas@oxygene.sk ***************************************************************************/ @@ -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: @@ -96,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()); @@ -119,13 +120,8 @@ public: CPPUNIT_ASSERT_EQUAL(44100, f.audioProperties()->sampleRate()); CPPUNIT_ASSERT(!f.audioProperties()->xingHeader()); - long 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 long last = f.lastFrameOffset(); + const MPEG::Header lastHeader(&f, last, false); CPPUNIT_ASSERT_EQUAL(28213LL, 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(2255LL, f.firstFrameOffset()); + CPPUNIT_ASSERT_EQUAL(6015LL, 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); diff --git a/tests/test_string.cpp b/tests/test_string.cpp index 464c51d5..3ca38a42 100644 --- a/tests/test_string.cpp +++ b/tests/test_string.cpp @@ -49,8 +49,9 @@ 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(testInvalidUTF8); CPPUNIT_TEST_SUITE_END(); public: @@ -174,6 +175,15 @@ public: const String s2(v2, String::UTF8); CPPUNIT_ASSERT_EQUAL(s2.data(String::UTF16), v1); + + 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() @@ -321,6 +331,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"; @@ -339,12 +356,26 @@ 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(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()); + CPPUNIT_ASSERT(String(ByteVector("\xED\xB0\x80\xED\xA0\x80"), String::UTF8).isEmpty()); } };