diff options
author | Devtools Arcadia <arcadia-devtools@yandex-team.ru> | 2022-02-07 18:08:42 +0300 |
---|---|---|
committer | Devtools Arcadia <arcadia-devtools@mous.vla.yp-c.yandex.net> | 2022-02-07 18:08:42 +0300 |
commit | 1110808a9d39d4b808aef724c861a2e1a38d2a69 (patch) | |
tree | e26c9fed0de5d9873cce7e00bc214573dc2195b7 /library/cpp/string_utils/base64/base64_ut.cpp | |
download | ydb-1110808a9d39d4b808aef724c861a2e1a38d2a69.tar.gz |
intermediate changes
ref:cde9a383711a11544ce7e107a78147fb96cc4029
Diffstat (limited to 'library/cpp/string_utils/base64/base64_ut.cpp')
-rw-r--r-- | library/cpp/string_utils/base64/base64_ut.cpp | 497 |
1 files changed, 497 insertions, 0 deletions
diff --git a/library/cpp/string_utils/base64/base64_ut.cpp b/library/cpp/string_utils/base64/base64_ut.cpp new file mode 100644 index 0000000000..bcc1e65879 --- /dev/null +++ b/library/cpp/string_utils/base64/base64_ut.cpp @@ -0,0 +1,497 @@ +#include "base64.h" + +#include <contrib/libs/base64/avx2/libbase64.h> +#include <contrib/libs/base64/neon32/libbase64.h> +#include <contrib/libs/base64/neon64/libbase64.h> +#include <contrib/libs/base64/plain32/libbase64.h> +#include <contrib/libs/base64/plain64/libbase64.h> +#include <contrib/libs/base64/ssse3/libbase64.h> + +#include <library/cpp/testing/unittest/registar.h> + +#include <util/generic/vector.h> +#include <util/random/fast.h> +#include <util/system/cpu_id.h> +#include <util/system/platform.h> + +#include <array> + +using namespace std::string_view_literals; + +#define BASE64_UT_DECLARE_BASE64_IMPL(prefix, encFunction, decFunction) \ + Y_DECLARE_UNUSED \ + static size_t prefix##Base64Decode(void* dst, const char* b, const char* e) { \ + const auto size = e - b; \ + Y_ENSURE(!(size % 4), "incorrect input length for base64 decode"); \ + \ + size_t outLen; \ + decFunction(b, size, (char*)dst, &outLen); \ + return outLen; \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline TStringBuf prefix##Base64Decode(const TStringBuf& src, void* dst) { \ + return TStringBuf((const char*)dst, ::NB64Etalon::prefix##Base64Decode(dst, src.begin(), src.end())); \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline void prefix##Base64Decode(const TStringBuf& src, TString& dst) { \ + dst.ReserveAndResize(Base64DecodeBufSize(src.size())); \ + dst.resize(::NB64Etalon::prefix##Base64Decode(src, dst.begin()).size()); \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline TString prefix##Base64Decode(const TStringBuf& s) { \ + TString ret; \ + prefix##Base64Decode(s, ret); \ + return ret; \ + } \ + \ + Y_DECLARE_UNUSED \ + static char* prefix##Base64Encode(char* outstr, const unsigned char* instr, size_t len) { \ + size_t outLen; \ + encFunction((char*)instr, len, outstr, &outLen); \ + *(outstr + outLen) = '\0'; \ + return outstr + outLen; \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline TStringBuf prefix##Base64Encode(const TStringBuf& src, void* tmp) { \ + return TStringBuf((const char*)tmp, ::NB64Etalon::prefix##Base64Encode((char*)tmp, (const unsigned char*)src.data(), src.size())); \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline void prefix##Base64Encode(const TStringBuf& src, TString& dst) { \ + dst.ReserveAndResize(Base64EncodeBufSize(src.size())); \ + dst.resize(::NB64Etalon::prefix##Base64Encode(src, dst.begin()).size()); \ + } \ + \ + Y_DECLARE_UNUSED \ + static inline TString prefix##Base64Encode(const TStringBuf& s) { \ + TString ret; \ + prefix##Base64Encode(s, ret); \ + return ret; \ + } + +namespace NB64Etalon { + BASE64_UT_DECLARE_BASE64_IMPL(PLAIN32, plain32_base64_encode, plain32_base64_decode); + BASE64_UT_DECLARE_BASE64_IMPL(PLAIN64, plain64_base64_encode, plain64_base64_decode); + BASE64_UT_DECLARE_BASE64_IMPL(NEON32, neon32_base64_encode, neon32_base64_decode); + BASE64_UT_DECLARE_BASE64_IMPL(NEON64, neon64_base64_encode, neon64_base64_decode); + BASE64_UT_DECLARE_BASE64_IMPL(AVX2, avx2_base64_encode, avx2_base64_decode); + BASE64_UT_DECLARE_BASE64_IMPL(SSSE3, ssse3_base64_encode, ssse3_base64_decode); + +#undef BASE64_UT_DECLARE_BASE64_IMPL + + struct TImpls { + enum EImpl : size_t { + PLAIN32_IMPL, + PLAIN64_IMPL, + NEON32_IMPL, + NEON64_IMPL, + AVX2_IMPL, + SSSE3_IMPL, + MAX_IMPL + }; + + using TEncodeF = void (*)(const TStringBuf&, TString&); + using TDecodeF = void (*)(const TStringBuf&, TString&); + + struct TImpl { + TEncodeF Encode = nullptr; + TDecodeF Decode = nullptr; + }; + + std::array<TImpl, MAX_IMPL> Impl; + + TImpls() { + Impl[PLAIN32_IMPL].Encode = PLAIN32Base64Encode; + Impl[PLAIN32_IMPL].Decode = PLAIN32Base64Decode; + Impl[PLAIN64_IMPL].Encode = PLAIN64Base64Encode; + Impl[PLAIN64_IMPL].Decode = PLAIN64Base64Decode; +#if defined(_arm32_) + Impl[NEON32_IMPL].Encode = NEON32Base64Encode; + Impl[NEON32_IMPL].Decode = NEON32Base64Decode; +#elif defined(_arm64_) + Impl[NEON64_IMPL].Encode = NEON64Base64Encode; + Impl[NEON64_IMPL].Decode = NEON64Base64Decode; +#elif defined(_x86_64_) + if (NX86::HaveSSSE3()) { + Impl[SSSE3_IMPL].Encode = SSSE3Base64Encode; + Impl[SSSE3_IMPL].Decode = SSSE3Base64Decode; + } + + if (NX86::HaveAVX2()) { + Impl[AVX2_IMPL].Encode = AVX2Base64Encode; + Impl[AVX2_IMPL].Decode = AVX2Base64Decode; + } +#else + ythrow yexception() << "Failed to identify the platform"; +#endif + } + }; + + TImpls GetImpls() { + static const TImpls IMPLS; + return IMPLS; + } +} + +template <> +void Out<NB64Etalon::TImpls::EImpl>(IOutputStream& o, typename TTypeTraits<NB64Etalon::TImpls::EImpl>::TFuncParam v) { + switch (v) { + case NB64Etalon::TImpls::PLAIN32_IMPL: + o << TStringBuf{"PLAIN32"}; + return; + case NB64Etalon::TImpls::PLAIN64_IMPL: + o << TStringBuf{"PLAIN64"}; + return; + case NB64Etalon::TImpls::NEON64_IMPL: + o << TStringBuf{"NEON64"}; + return; + case NB64Etalon::TImpls::NEON32_IMPL: + o << TStringBuf{"NEON32"}; + return; + case NB64Etalon::TImpls::SSSE3_IMPL: + o << TStringBuf{"SSSE3"}; + return; + case NB64Etalon::TImpls::AVX2_IMPL: + o << TStringBuf{"AVX2"}; + return; + default: + ythrow yexception() << "invalid"; + } +} + +static void TestEncodeDecodeIntoString(const TString& plain, const TString& encoded, const TString& encodedUrl) { + TString a, b; + + Base64Encode(plain, a); + UNIT_ASSERT_VALUES_EQUAL(a, encoded); + + Base64Decode(a, b); + UNIT_ASSERT_VALUES_EQUAL(b, plain); + + Base64EncodeUrl(plain, a); + UNIT_ASSERT_VALUES_EQUAL(a, encodedUrl); + + Base64Decode(a, b); + UNIT_ASSERT_VALUES_EQUAL(b, plain); +} + +static void TestEncodeStrictDecodeIntoString(const TString& plain, const TString& encoded, const TString& encodedUrl) { + TString a, b; + + Base64Encode(plain, a); + UNIT_ASSERT_VALUES_EQUAL(a, encoded); + + Base64StrictDecode(a, b); + UNIT_ASSERT_VALUES_EQUAL(b, plain); + + Base64EncodeUrl(plain, a); + UNIT_ASSERT_VALUES_EQUAL(a, encodedUrl); + + Base64StrictDecode(a, b); + UNIT_ASSERT_VALUES_EQUAL(b, plain); +} + +Y_UNIT_TEST_SUITE(TBase64) { + Y_UNIT_TEST(TestEncode) { + UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12z"), "MTJ6"); + UNIT_ASSERT_VALUES_EQUAL(Base64Encode("123"), "MTIz"); + UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12"), "MTI="); + UNIT_ASSERT_VALUES_EQUAL(Base64Encode("1"), "MQ=="); + } + + Y_UNIT_TEST(TestIntoString) { + { + TString str; + for (size_t i = 0; i < 256; ++i) + str += char(i); + + const TString base64 = + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy" + "gpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9Q" + "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH" + "l6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch" + "oqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIyc" + "rLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy" + "8/T19vf4+fr7/P3+/w=="; + const TString base64Url = + "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy" + "gpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9Q" + "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH" + "l6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch" + "oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIyc" + "rLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy" + "8_T19vf4-fr7_P3-_w,,"; + + TestEncodeDecodeIntoString(str, base64, base64Url); + TestEncodeStrictDecodeIntoString(str, base64, base64Url); + } + + { + const TString str = "http://yandex.ru:1234/request?param=value&lll=fff#fragment"; + + const TString base64 = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q/cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA=="; + const TString base64Url = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q_cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA,,"; + + TestEncodeDecodeIntoString(str, base64, base64Url); + TestEncodeStrictDecodeIntoString(str, base64, base64Url); + } + } + + Y_UNIT_TEST(TestDecode) { + UNIT_ASSERT_EXCEPTION(Base64Decode("a"), yexception); + UNIT_ASSERT_EXCEPTION(Base64StrictDecode("a"), yexception); + + UNIT_ASSERT_VALUES_EQUAL(Base64Decode(""), ""); + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(""), ""); + + UNIT_ASSERT_VALUES_EQUAL(Base64Decode("MTI="), "12"); + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("MTI="), "12"); + + UNIT_ASSERT_VALUES_EQUAL(Base64Decode("QQ=="), "A"); + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("QQ=="), "A"); + + UNIT_ASSERT_EXCEPTION(Base64StrictDecode("M=I="), yexception); + + UNIT_ASSERT_VALUES_EQUAL(Base64Decode("dnluZHg="), "vyndx"); + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg="), "vyndx"); + + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg=dmlkZW8="), "vyndxvideo"); + + UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHR0cDovL2ltZy5tZWdhLXBvcm5vLnJ1Lw=a"), yexception); + + UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHh=="), yexception); + UNIT_ASSERT_EXCEPTION(Base64StrictDecode("\1\1\1\2"), yexception); + } + + Y_UNIT_TEST(TestDecodeUneven) { + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven(""), ""); + + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("YWFh"), "aaa"); + + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI="), "12"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI,"), "12"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI"), "12"); + + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ=="), "A"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ,,"), "A"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ"), "A"); + + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg="), "vyndx"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg,"), "vyndx"); + UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg"), "vyndx"); + } + + Y_UNIT_TEST(TestDecodeRandom) { + TString input; + constexpr size_t testSize = 240000; + for (size_t i = 0; i < testSize; ++i) { + input.push_back(rand() % 256); + } + TString output; + TString encoded = Base64Encode(input); + UNIT_ASSERT_VALUES_EQUAL(Base64Decode(encoded), input); + UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(encoded), input); + } + + Y_UNIT_TEST(TestAllPossibleOctets) { + const TString x("\0\x01\x02\x03\x04\x05\x06\x07\b\t\n\x0B\f\r\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\x7F"sv); + const TString xEnc = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestTwoPaddingCharacters) { + const TString x("a"); + const TString xEnc = "YQ=="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestOnePaddingCharacter) { + const TString x("aa"); + const TString xEnc = "YWE="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestNoPaddingCharacters) { + const TString x("aaa"); + const TString xEnc = "YWFh"; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestTrailingZero) { + const TString x("foo\0"sv); + const TString xEnc = "Zm9vAA=="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestTwoTrailingZeroes) { + const TString x("foo\0\0"sv); + const TString xEnc = "Zm9vAAA="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestZero) { + const TString x("\0"sv); + const TString xEnc = "AA=="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestSymbolsAfterZero) { + const TString x("\0a"sv); + const TString xEnc = "AGE="; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestEmptyString) { + const TString x = ""; + const TString xEnc = ""; + const TString y = Base64Decode(xEnc); + const TString yEnc = Base64Encode(x); + UNIT_ASSERT_VALUES_EQUAL(x, y); + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + } + + Y_UNIT_TEST(TestBackendsConsistencyOnRandomData) { + constexpr size_t TEST_CASES_COUNT = 1000; + constexpr size_t MAX_DATA_SIZE = 1000; + TFastRng<ui32> prng{42}; + TVector<TString> xs{TEST_CASES_COUNT}; + TString xEnc; + TString xDec; + TString yEnc; + TString yDec; + + for (auto& x : xs) { + const size_t size = prng() % MAX_DATA_SIZE; + for (size_t j = 0; j < size; ++j) { + x += static_cast<char>(prng() % 256); + } + } + + static const auto IMPLS = NB64Etalon::GetImpls(); + for (size_t i = 0; i < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++i) { + for (size_t j = 0; j < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++j) { + const auto ei = static_cast<NB64Etalon::TImpls::EImpl>(i); + const auto ej = static_cast<NB64Etalon::TImpls::EImpl>(j); + const auto impl = IMPLS.Impl[i]; + const auto otherImpl = IMPLS.Impl[j]; + if (!impl.Encode && !impl.Decode || !otherImpl.Encode && !otherImpl.Decode) { + continue; + } + + for (const auto& x : xs) { + impl.Encode(x, xEnc); + impl.Decode(xEnc, xDec); + Y_ENSURE(x == xDec, "something is wrong with " << ei << " implementation"); + + otherImpl.Encode(x, yEnc); + otherImpl.Decode(xEnc, yDec); + Y_ENSURE(x == yDec, "something is wrong with " << ej << " implementation"); + + UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc); + UNIT_ASSERT_VALUES_EQUAL(xDec, yDec); + } + } + } + } + + Y_UNIT_TEST(TestIfEncodedDataIsZeroTerminatedOnRandomData) { + constexpr size_t TEST_CASES_COUNT = 1000; + constexpr size_t MAX_DATA_SIZE = 1000; + TFastRng<ui32> prng{42}; + TString x; + TVector<char> buf; + for (size_t i = 0; i < TEST_CASES_COUNT; ++i) { + const size_t size = prng() % MAX_DATA_SIZE; + x.clear(); + for (size_t j = 0; j < size; ++j) { + x += static_cast<char>(prng() % 256); + } + + buf.assign(Base64EncodeBufSize(x.size()), Max<char>()); + const auto* const xEncEnd = Base64Encode(buf.data(), (const unsigned char*)x.data(), x.size()); + UNIT_ASSERT_VALUES_EQUAL(*xEncEnd, '\0'); + } + } + + Y_UNIT_TEST(TestDecodeURLEncodedNoPadding) { + const auto x = "123"; + const auto xDec = Base64Decode("MTIz"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeURLEncodedOnePadding) { + const auto x = "12"; + const auto xDec = Base64Decode("MTI,"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeURLEncodedTwoPadding) { + const auto x = "1"; + const auto xDec = Base64Decode("MQ,,"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeNoPaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?a"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeOnePaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8="); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeTwoPaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?aa"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ=="); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeURLEncodedNoPaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?a"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeURLEncodedOnePaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8,"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } + + Y_UNIT_TEST(TestDecodeURLEncodedTwoPaddingLongString) { + const auto x = "How do I convert between big-endian and little-endian values in C++?aa"; + const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ,,"); + UNIT_ASSERT_VALUES_EQUAL(x, xDec); + } +} |