From a9acca5d817932e9362ed28b9641b05a1aac0683 Mon Sep 17 00:00:00 2001 From: Tsuda Kageyu Date: Thu, 18 Feb 2016 03:07:38 +0900 Subject: [PATCH] Decode unsynchronized ID3v2 frames efficiently. It makes a great difference when decoding huge unsynchronized ID3v2 frames. --- NEWS | 1 + taglib/mpeg/id3v2/id3v2synchdata.cpp | 26 ++++++++++++++++++++++---- tests/test_synchdata.cpp | 9 +++++++++ 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 74695376..5fbfb7ee 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,7 @@ TagLib 1.11 (Jan 30, 2016) * Better handling of PCM WAV files with a 'fact' chunk. * Better handling of corrupted APE tags. + * Efficient decoding of unsynchronized ID3v2 frames. * Fixed text encoding when saving certain frames in ID3v2.3 tags. * Several smaller bug fixes and performance improvements. diff --git a/taglib/mpeg/id3v2/id3v2synchdata.cpp b/taglib/mpeg/id3v2/id3v2synchdata.cpp index b16c1987..5ef419ec 100644 --- a/taglib/mpeg/id3v2/id3v2synchdata.cpp +++ b/taglib/mpeg/id3v2/id3v2synchdata.cpp @@ -74,11 +74,29 @@ ByteVector SynchData::fromUInt(unsigned int value) ByteVector SynchData::decode(const ByteVector &data) { + // We have this optimized method instead of using ByteVector::replace(), + // since it makes a great difference when decoding huge unsynchronized frames. + + if(data.size() < 2) + return data; + ByteVector result = data; - ByteVector pattern(2, char(0)); - pattern[0] = '\xFF'; - pattern[1] = '\x00'; + char *begin = result.data(); + char *end = begin + result.size(); - return result.replace(pattern, '\xFF'); + char *dst = begin; + const char *src = begin; + + do { + *dst++ = *src++; + + if(*(src - 1) == '\xff' && *src == '\x00') + src++; + + } while (src < end); + + result.resize(static_cast(dst - begin)); + + return result; } diff --git a/tests/test_synchdata.cpp b/tests/test_synchdata.cpp index 4cffee14..bbd74d8b 100644 --- a/tests/test_synchdata.cpp +++ b/tests/test_synchdata.cpp @@ -40,6 +40,7 @@ class TestID3v2SynchData : public CppUnit::TestFixture CPPUNIT_TEST(testToUIntBrokenAndTooLarge); CPPUNIT_TEST(testDecode1); CPPUNIT_TEST(testDecode2); + CPPUNIT_TEST(testDecode3); CPPUNIT_TEST_SUITE_END(); public: @@ -104,6 +105,14 @@ public: CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\x44", 2), a); } + void testDecode3() + { + ByteVector a("\xff\xff\x00", 3); + a = ID3v2::SynchData::decode(a); + CPPUNIT_ASSERT_EQUAL((unsigned int)2, a.size()); + CPPUNIT_ASSERT_EQUAL(ByteVector("\xff\xff", 2), a); + } + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestID3v2SynchData);