diff --git a/taglib/toolkit/tbytevector.cpp b/taglib/toolkit/tbytevector.cpp index 6262bec6..eb559390 100644 --- a/taglib/toolkit/tbytevector.cpp +++ b/taglib/toolkit/tbytevector.cpp @@ -950,6 +950,98 @@ ByteVector ByteVector::toHex() const return encoded; } + + + + +ByteVector & ByteVector::fromBase64() +{ + static const unsigned char base64[256]={ + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x3e,0x80,0x80,0x80,0x3f, + 0x34,0x35,0x36,0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x80,0x80,0x80,0xff,0x80,0x80, + 0x80,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e, + 0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19,0x80,0x80,0x80,0x80,0x80, + 0x80,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27,0x28, + 0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80, + 0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80 + }; + + detach(); + uint len = size(); + const unsigned char * src = (unsigned char*) data(); + unsigned char * dst = (unsigned char*) data(); + while(4<=len) { + if(base64[src[0]]==0x80) break; + if(base64[src[1]]==0x80) break; + if(base64[src[2]]==0x80) break; + if(base64[src[3]]==0x80) break; + *dst++=((base64[src[0]]<<2)&0xfc)|((base64[src[1]]>>4)&0x03); + if(src[2]!='=') { + *dst++=((base64[src[1]]&0x0f)<<4)|((base64[src[2]]>>2)&0x0f); + if(src[3]!='=') { + *dst++=((base64[src[2]]&0x03)<<6)|(base64[src[3]]&0x3f); + } + else { + break; + } + } + else { + break; + } + src+=4; + len-=4; + } + resize(dst-(unsigned char*)data()); + return *this; +} + + + + +ByteVector ByteVector::toBase64() const +{ + static const char alphabet[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + if (size()) { + uint len=size(); + ByteVector output(4 * ((len-1)/3+1)); // note roundup + + const char * src = data(); + char * dst = output.data(); + while(3<=len) { + *dst++=alphabet[(src[0]>>2)&0x3f]; + *dst++=alphabet[((src[0]&0x03)<<4)|((src[1]>>4)&0x0f)]; + *dst++=alphabet[((src[1]&0x0f)<<2)|((src[2]>>6)&0x03)]; + *dst++=alphabet[src[2]&0x3f]; + src+=3; + len-=3; + } + if(len) { + *dst++=alphabet[(src[0]>>2)&0x3f]; + if(len>1) { + *dst++=alphabet[((src[0]&0x03)<<4)|((src[1]>>4)&0x0f)]; + *dst++=alphabet[((src[1]&0x0f)<<2)]; + } + else { + *dst++=alphabet[(src[0]&0x03)<<4]; + *dst++='='; + } + *dst++='='; + } + return output; + } + return ByteVector(); +} + + //////////////////////////////////////////////////////////////////////////////// // protected members //////////////////////////////////////////////////////////////////////////////// diff --git a/taglib/toolkit/tbytevector.h b/taglib/toolkit/tbytevector.h index 793b414c..0f96c77f 100644 --- a/taglib/toolkit/tbytevector.h +++ b/taglib/toolkit/tbytevector.h @@ -573,6 +573,19 @@ namespace TagLib { */ ByteVector toHex() const; + /*! + * Returns a base64 encoded copy of the byte vector + */ + ByteVector toBase64() const; + + /*! + * Decodes the base64 encoded byte vector in-memory. Returns a reference + * to this vector. Calls detach before decoding. + */ + ByteVector & fromBase64(); + + + protected: /* * If this ByteVector is being shared via implicit sharing, do a deep copy diff --git a/tests/test_bytevector.cpp b/tests/test_bytevector.cpp index 1c2ca904..19f51518 100644 --- a/tests/test_bytevector.cpp +++ b/tests/test_bytevector.cpp @@ -43,6 +43,7 @@ class TestByteVector : public CppUnit::TestFixture CPPUNIT_TEST(testReplace); CPPUNIT_TEST(testIterator); CPPUNIT_TEST(testResize); + CPPUNIT_TEST(testBase64); CPPUNIT_TEST_SUITE_END(); public: @@ -379,6 +380,45 @@ public: CPPUNIT_ASSERT_EQUAL(-1, c.find('C')); } + void testBase64() + { + ByteVector sempty; + ByteVector t0("a"); // test 1 byte + ByteVector t1("any carnal pleasure."); + ByteVector t2("any carnal pleasure"); + ByteVector t3("any carnal pleasur"); + ByteVector s0("a"); // test 1 byte + ByteVector s1("any carnal pleasure."); + ByteVector s2("any carnal pleasure"); + ByteVector s3("any carnal pleasur"); + ByteVector eempty; + ByteVector e0("YQ=="); + ByteVector e1("YW55IGNhcm5hbCBwbGVhc3VyZS4="); + ByteVector e2("YW55IGNhcm5hbCBwbGVhc3VyZQ=="); + ByteVector e3("YW55IGNhcm5hbCBwbGVhc3Vy"); + + // Encode + CPPUNIT_ASSERT_EQUAL(eempty, sempty.toBase64()); + CPPUNIT_ASSERT_EQUAL(e0, s0.toBase64()); + CPPUNIT_ASSERT_EQUAL(e1, s1.toBase64()); + CPPUNIT_ASSERT_EQUAL(e2, s2.toBase64()); + CPPUNIT_ASSERT_EQUAL(e3, s3.toBase64()); + + // Decode + CPPUNIT_ASSERT_EQUAL(sempty, eempty.toBase64()); + CPPUNIT_ASSERT_EQUAL(s0, e0.fromBase64()); + CPPUNIT_ASSERT_EQUAL(s1, e1.fromBase64()); + CPPUNIT_ASSERT_EQUAL(s2, e2.fromBase64()); + CPPUNIT_ASSERT_EQUAL(s3, e3.fromBase64()); + + CPPUNIT_ASSERT_EQUAL(t0, s0.toBase64().fromBase64()); + CPPUNIT_ASSERT_EQUAL(t1, s1.toBase64().fromBase64()); + CPPUNIT_ASSERT_EQUAL(t2, s2.toBase64().fromBase64()); + CPPUNIT_ASSERT_EQUAL(t3, s3.toBase64().fromBase64()); + + } + + }; CPPUNIT_TEST_SUITE_REGISTRATION(TestByteVector);