base64_ut.cpp 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  1. #include "base64.h"
  2. #include <contrib/libs/base64/avx2/libbase64.h>
  3. #include <contrib/libs/base64/neon32/libbase64.h>
  4. #include <contrib/libs/base64/neon64/libbase64.h>
  5. #include <contrib/libs/base64/plain32/libbase64.h>
  6. #include <contrib/libs/base64/plain64/libbase64.h>
  7. #include <contrib/libs/base64/ssse3/libbase64.h>
  8. #include <library/cpp/testing/unittest/registar.h>
  9. #include <util/generic/vector.h>
  10. #include <util/random/fast.h>
  11. #include <util/system/cpu_id.h>
  12. #include <util/system/platform.h>
  13. #include <array>
  14. using namespace std::string_view_literals;
  15. #define BASE64_UT_DECLARE_BASE64_IMPL(prefix, encFunction, decFunction) \
  16. Y_DECLARE_UNUSED \
  17. static size_t prefix##Base64Decode(void* dst, const char* b, const char* e) { \
  18. const auto size = e - b; \
  19. Y_ENSURE(!(size % 4), "incorrect input length for base64 decode"); \
  20. \
  21. size_t outLen; \
  22. decFunction(b, size, (char*)dst, &outLen); \
  23. return outLen; \
  24. } \
  25. \
  26. Y_DECLARE_UNUSED \
  27. static inline TStringBuf prefix##Base64Decode(const TStringBuf& src, void* dst) { \
  28. return TStringBuf((const char*)dst, ::NB64Etalon::prefix##Base64Decode(dst, src.begin(), src.end())); \
  29. } \
  30. \
  31. Y_DECLARE_UNUSED \
  32. static inline void prefix##Base64Decode(const TStringBuf& src, TString& dst) { \
  33. dst.ReserveAndResize(Base64DecodeBufSize(src.size())); \
  34. dst.resize(::NB64Etalon::prefix##Base64Decode(src, dst.begin()).size()); \
  35. } \
  36. \
  37. Y_DECLARE_UNUSED \
  38. static inline TString prefix##Base64Decode(const TStringBuf& s) { \
  39. TString ret; \
  40. prefix##Base64Decode(s, ret); \
  41. return ret; \
  42. } \
  43. \
  44. Y_DECLARE_UNUSED \
  45. static char* prefix##Base64Encode(char* outstr, const unsigned char* instr, size_t len) { \
  46. size_t outLen; \
  47. encFunction((char*)instr, len, outstr, &outLen); \
  48. *(outstr + outLen) = '\0'; \
  49. return outstr + outLen; \
  50. } \
  51. \
  52. Y_DECLARE_UNUSED \
  53. static inline TStringBuf prefix##Base64Encode(const TStringBuf& src, void* tmp) { \
  54. return TStringBuf((const char*)tmp, ::NB64Etalon::prefix##Base64Encode((char*)tmp, (const unsigned char*)src.data(), src.size())); \
  55. } \
  56. \
  57. Y_DECLARE_UNUSED \
  58. static inline void prefix##Base64Encode(const TStringBuf& src, TString& dst) { \
  59. dst.ReserveAndResize(Base64EncodeBufSize(src.size())); \
  60. dst.resize(::NB64Etalon::prefix##Base64Encode(src, dst.begin()).size()); \
  61. } \
  62. \
  63. Y_DECLARE_UNUSED \
  64. static inline TString prefix##Base64Encode(const TStringBuf& s) { \
  65. TString ret; \
  66. prefix##Base64Encode(s, ret); \
  67. return ret; \
  68. }
  69. namespace NB64Etalon {
  70. BASE64_UT_DECLARE_BASE64_IMPL(PLAIN32, plain32_base64_encode, plain32_base64_decode)
  71. BASE64_UT_DECLARE_BASE64_IMPL(PLAIN64, plain64_base64_encode, plain64_base64_decode)
  72. BASE64_UT_DECLARE_BASE64_IMPL(NEON32, neon32_base64_encode, neon32_base64_decode)
  73. BASE64_UT_DECLARE_BASE64_IMPL(NEON64, neon64_base64_encode, neon64_base64_decode)
  74. BASE64_UT_DECLARE_BASE64_IMPL(AVX2, avx2_base64_encode, avx2_base64_decode)
  75. BASE64_UT_DECLARE_BASE64_IMPL(SSSE3, ssse3_base64_encode, ssse3_base64_decode)
  76. #undef BASE64_UT_DECLARE_BASE64_IMPL
  77. struct TImpls {
  78. enum EImpl : size_t {
  79. PLAIN32_IMPL,
  80. PLAIN64_IMPL,
  81. NEON32_IMPL,
  82. NEON64_IMPL,
  83. AVX2_IMPL,
  84. SSSE3_IMPL,
  85. MAX_IMPL
  86. };
  87. using TEncodeF = void (*)(const TStringBuf&, TString&);
  88. using TDecodeF = void (*)(const TStringBuf&, TString&);
  89. struct TImpl {
  90. TEncodeF Encode = nullptr;
  91. TDecodeF Decode = nullptr;
  92. };
  93. std::array<TImpl, MAX_IMPL> Impl;
  94. TImpls() {
  95. Impl[PLAIN32_IMPL].Encode = PLAIN32Base64Encode;
  96. Impl[PLAIN32_IMPL].Decode = PLAIN32Base64Decode;
  97. Impl[PLAIN64_IMPL].Encode = PLAIN64Base64Encode;
  98. Impl[PLAIN64_IMPL].Decode = PLAIN64Base64Decode;
  99. #if defined(_arm32_)
  100. Impl[NEON32_IMPL].Encode = NEON32Base64Encode;
  101. Impl[NEON32_IMPL].Decode = NEON32Base64Decode;
  102. #elif defined(_arm64_)
  103. Impl[NEON64_IMPL].Encode = NEON64Base64Encode;
  104. Impl[NEON64_IMPL].Decode = NEON64Base64Decode;
  105. #elif defined(_x86_64_)
  106. if (NX86::HaveSSSE3()) {
  107. Impl[SSSE3_IMPL].Encode = SSSE3Base64Encode;
  108. Impl[SSSE3_IMPL].Decode = SSSE3Base64Decode;
  109. }
  110. if (NX86::HaveAVX2()) {
  111. Impl[AVX2_IMPL].Encode = AVX2Base64Encode;
  112. Impl[AVX2_IMPL].Decode = AVX2Base64Decode;
  113. }
  114. #else
  115. ythrow yexception() << "Failed to identify the platform";
  116. #endif
  117. }
  118. };
  119. TImpls GetImpls() {
  120. static const TImpls IMPLS;
  121. return IMPLS;
  122. }
  123. }
  124. template <>
  125. void Out<NB64Etalon::TImpls::EImpl>(IOutputStream& o, typename TTypeTraits<NB64Etalon::TImpls::EImpl>::TFuncParam v) {
  126. switch (v) {
  127. case NB64Etalon::TImpls::PLAIN32_IMPL:
  128. o << TStringBuf{"PLAIN32"};
  129. return;
  130. case NB64Etalon::TImpls::PLAIN64_IMPL:
  131. o << TStringBuf{"PLAIN64"};
  132. return;
  133. case NB64Etalon::TImpls::NEON64_IMPL:
  134. o << TStringBuf{"NEON64"};
  135. return;
  136. case NB64Etalon::TImpls::NEON32_IMPL:
  137. o << TStringBuf{"NEON32"};
  138. return;
  139. case NB64Etalon::TImpls::SSSE3_IMPL:
  140. o << TStringBuf{"SSSE3"};
  141. return;
  142. case NB64Etalon::TImpls::AVX2_IMPL:
  143. o << TStringBuf{"AVX2"};
  144. return;
  145. default:
  146. ythrow yexception() << "invalid";
  147. }
  148. }
  149. static void TestEncodeDecodeIntoString(const TString& plain, const TString& encoded, const TString& encodedUrl, const TString& encodedUrlNoPadding) {
  150. TString a, b;
  151. Base64Encode(plain, a);
  152. UNIT_ASSERT_VALUES_EQUAL(a, encoded);
  153. Base64Decode(a, b);
  154. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  155. Base64EncodeUrl(plain, a);
  156. UNIT_ASSERT_VALUES_EQUAL(a, encodedUrl);
  157. Base64Decode(a, b);
  158. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  159. Base64EncodeUrlNoPadding(plain, a);
  160. UNIT_ASSERT_VALUES_EQUAL(a, encodedUrlNoPadding);
  161. TString c = Base64DecodeUneven(a);
  162. UNIT_ASSERT_VALUES_EQUAL(c, plain);
  163. }
  164. static void TestEncodeStrictDecodeIntoString(const TString& plain, const TString& encoded, const TString& encodedUrl) {
  165. TString a, b;
  166. Base64Encode(plain, a);
  167. UNIT_ASSERT_VALUES_EQUAL(a, encoded);
  168. Base64StrictDecode(a, b);
  169. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  170. Base64EncodeUrl(plain, a);
  171. UNIT_ASSERT_VALUES_EQUAL(a, encodedUrl);
  172. Base64StrictDecode(a, b);
  173. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  174. }
  175. Y_UNIT_TEST_SUITE(TBase64) {
  176. Y_UNIT_TEST(TestEncode) {
  177. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12z"), "MTJ6");
  178. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("123"), "MTIz");
  179. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12"), "MTI=");
  180. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("1"), "MQ==");
  181. }
  182. Y_UNIT_TEST(TestIntoString) {
  183. {
  184. TString str;
  185. for (size_t i = 0; i < 256; ++i)
  186. str += char(i);
  187. const TString base64 =
  188. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  189. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9Q"
  190. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  191. "l6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  192. "oqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIyc"
  193. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy"
  194. "8/T19vf4+fr7/P3+/w==";
  195. const TString base64Url =
  196. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  197. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9Q"
  198. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  199. "l6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  200. "oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIyc"
  201. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy"
  202. "8_T19vf4-fr7_P3-_w,,";
  203. const TString base64UrlWithoutPadding =
  204. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  205. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9Q"
  206. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  207. "l6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  208. "oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIyc"
  209. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy"
  210. "8_T19vf4-fr7_P3-_w";
  211. TestEncodeDecodeIntoString(str, base64, base64Url, base64UrlWithoutPadding);
  212. TestEncodeStrictDecodeIntoString(str, base64, base64Url);
  213. }
  214. {
  215. const TString str = "http://yandex.ru:1234/request?param=value&lll=fff#fragment";
  216. const TString base64 = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q/cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA==";
  217. const TString base64Url = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q_cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA,,";
  218. const TString base64UrlWithoutPadding = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q_cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA";
  219. TestEncodeDecodeIntoString(str, base64, base64Url, base64UrlWithoutPadding);
  220. TestEncodeStrictDecodeIntoString(str, base64, base64Url);
  221. }
  222. }
  223. Y_UNIT_TEST(TestDecode) {
  224. UNIT_ASSERT_EXCEPTION(Base64Decode("a"), yexception);
  225. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("a"), yexception);
  226. UNIT_ASSERT_VALUES_EQUAL(Base64Decode(""), "");
  227. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(""), "");
  228. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("MTI="), "12");
  229. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("MTI="), "12");
  230. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("QQ=="), "A");
  231. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("QQ=="), "A");
  232. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("M=I="), yexception);
  233. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("dnluZHg="), "vyndx");
  234. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg="), "vyndx");
  235. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg=dmlkZW8="), "vyndxvideo");
  236. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHR0cDovL2ltZy5tZWdhLXBvcm5vLnJ1Lw=a"), yexception);
  237. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHh=="), yexception);
  238. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("\1\1\1\2"), yexception);
  239. }
  240. Y_UNIT_TEST(TestDecodeUneven) {
  241. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven(""), "");
  242. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("YWFh"), "aaa");
  243. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI="), "12");
  244. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI,"), "12");
  245. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI"), "12");
  246. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ=="), "A");
  247. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ,,"), "A");
  248. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ"), "A");
  249. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg="), "vyndx");
  250. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg,"), "vyndx");
  251. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg"), "vyndx");
  252. }
  253. Y_UNIT_TEST(TestDecodeRandom) {
  254. TString input;
  255. constexpr size_t testSize = 240000;
  256. for (size_t i = 0; i < testSize; ++i) {
  257. input.push_back(rand() % 256);
  258. }
  259. TString output;
  260. TString encoded = Base64Encode(input);
  261. TString encodedUrl = TString::Uninitialized(Base64EncodeBufSize(input.length()));
  262. Base64EncodeUrlNoPadding(input, encodedUrl);
  263. UNIT_ASSERT_VALUES_EQUAL(Base64Decode(encoded), input);
  264. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(encoded), input);
  265. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven(encodedUrl), input);
  266. }
  267. Y_UNIT_TEST(TestAllPossibleOctets) {
  268. 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);
  269. const TString xEnc = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=";
  270. const TString y = Base64Decode(xEnc);
  271. const TString yEnc = Base64Encode(x);
  272. UNIT_ASSERT_VALUES_EQUAL(x, y);
  273. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  274. }
  275. Y_UNIT_TEST(TestTwoPaddingCharacters) {
  276. const TString x("a");
  277. const TString xEnc = "YQ==";
  278. const TString y = Base64Decode(xEnc);
  279. const TString yEnc = Base64Encode(x);
  280. UNIT_ASSERT_VALUES_EQUAL(x, y);
  281. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  282. }
  283. Y_UNIT_TEST(TestOnePaddingCharacter) {
  284. const TString x("aa");
  285. const TString xEnc = "YWE=";
  286. const TString y = Base64Decode(xEnc);
  287. const TString yEnc = Base64Encode(x);
  288. UNIT_ASSERT_VALUES_EQUAL(x, y);
  289. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  290. }
  291. Y_UNIT_TEST(TestNoPaddingCharacters) {
  292. const TString x("aaa");
  293. const TString xEnc = "YWFh";
  294. const TString y = Base64Decode(xEnc);
  295. const TString yEnc = Base64Encode(x);
  296. UNIT_ASSERT_VALUES_EQUAL(x, y);
  297. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  298. }
  299. Y_UNIT_TEST(TestTrailingZero) {
  300. const TString x("foo\0"sv);
  301. const TString xEnc = "Zm9vAA==";
  302. const TString y = Base64Decode(xEnc);
  303. const TString yEnc = Base64Encode(x);
  304. UNIT_ASSERT_VALUES_EQUAL(x, y);
  305. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  306. }
  307. Y_UNIT_TEST(TestTwoTrailingZeroes) {
  308. const TString x("foo\0\0"sv);
  309. const TString xEnc = "Zm9vAAA=";
  310. const TString y = Base64Decode(xEnc);
  311. const TString yEnc = Base64Encode(x);
  312. UNIT_ASSERT_VALUES_EQUAL(x, y);
  313. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  314. }
  315. Y_UNIT_TEST(TestZero) {
  316. const TString x("\0"sv);
  317. const TString xEnc = "AA==";
  318. const TString y = Base64Decode(xEnc);
  319. const TString yEnc = Base64Encode(x);
  320. UNIT_ASSERT_VALUES_EQUAL(x, y);
  321. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  322. }
  323. Y_UNIT_TEST(TestSymbolsAfterZero) {
  324. const TString x("\0a"sv);
  325. const TString xEnc = "AGE=";
  326. const TString y = Base64Decode(xEnc);
  327. const TString yEnc = Base64Encode(x);
  328. UNIT_ASSERT_VALUES_EQUAL(x, y);
  329. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  330. }
  331. Y_UNIT_TEST(TestEmptyString) {
  332. const TString x = "";
  333. const TString xEnc = "";
  334. const TString y = Base64Decode(xEnc);
  335. const TString yEnc = Base64Encode(x);
  336. UNIT_ASSERT_VALUES_EQUAL(x, y);
  337. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  338. }
  339. Y_UNIT_TEST(TestBackendsConsistencyOnRandomData) {
  340. constexpr size_t TEST_CASES_COUNT = 1000;
  341. constexpr size_t MAX_DATA_SIZE = 1000;
  342. TFastRng<ui32> prng{42};
  343. TVector<TString> xs{TEST_CASES_COUNT};
  344. TString xEnc;
  345. TString xDec;
  346. TString yEnc;
  347. TString yDec;
  348. for (auto& x : xs) {
  349. const size_t size = prng() % MAX_DATA_SIZE;
  350. for (size_t j = 0; j < size; ++j) {
  351. x += static_cast<char>(prng() % 256);
  352. }
  353. }
  354. static const auto IMPLS = NB64Etalon::GetImpls();
  355. for (size_t i = 0; i < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++i) {
  356. for (size_t j = 0; j < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++j) {
  357. const auto ei = static_cast<NB64Etalon::TImpls::EImpl>(i);
  358. const auto ej = static_cast<NB64Etalon::TImpls::EImpl>(j);
  359. const auto impl = IMPLS.Impl[i];
  360. const auto otherImpl = IMPLS.Impl[j];
  361. if (!impl.Encode && !impl.Decode || !otherImpl.Encode && !otherImpl.Decode) {
  362. continue;
  363. }
  364. for (const auto& x : xs) {
  365. impl.Encode(x, xEnc);
  366. impl.Decode(xEnc, xDec);
  367. Y_ENSURE(x == xDec, "something is wrong with " << ei << " implementation");
  368. otherImpl.Encode(x, yEnc);
  369. otherImpl.Decode(xEnc, yDec);
  370. Y_ENSURE(x == yDec, "something is wrong with " << ej << " implementation");
  371. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  372. UNIT_ASSERT_VALUES_EQUAL(xDec, yDec);
  373. }
  374. }
  375. }
  376. }
  377. Y_UNIT_TEST(TestIfEncodedDataIsZeroTerminatedOnRandomData) {
  378. constexpr size_t TEST_CASES_COUNT = 1000;
  379. constexpr size_t MAX_DATA_SIZE = 1000;
  380. TFastRng<ui32> prng{42};
  381. TString x;
  382. TVector<char> buf;
  383. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  384. const size_t size = prng() % MAX_DATA_SIZE;
  385. x.clear();
  386. for (size_t j = 0; j < size; ++j) {
  387. x += static_cast<char>(prng() % 256);
  388. }
  389. buf.assign(Base64EncodeBufSize(x.size()), Max<char>());
  390. const auto* const xEncEnd = Base64Encode(buf.data(), (const unsigned char*)x.data(), x.size());
  391. UNIT_ASSERT_VALUES_EQUAL(*xEncEnd, '\0');
  392. }
  393. }
  394. Y_UNIT_TEST(TestDecodeURLEncodedNoPadding) {
  395. const auto x = "123";
  396. const auto xDec = Base64Decode("MTIz");
  397. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  398. }
  399. Y_UNIT_TEST(TestDecodeURLEncodedOnePadding) {
  400. const auto x = "12";
  401. const auto xDec = Base64Decode("MTI,");
  402. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  403. }
  404. Y_UNIT_TEST(TestDecodeURLEncodedTwoPadding) {
  405. const auto x = "1";
  406. const auto xDec = Base64Decode("MQ,,");
  407. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  408. }
  409. Y_UNIT_TEST(TestDecodeURLEncodedWithoutPadding) {
  410. const auto x = "1";
  411. const auto xDec = Base64DecodeUneven("MQ");
  412. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  413. }
  414. Y_UNIT_TEST(TestDecodeNoPaddingLongString) {
  415. const auto x = "How do I convert between big-endian and little-endian values in C++?a";
  416. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h");
  417. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  418. }
  419. Y_UNIT_TEST(TestDecodeOnePaddingLongString) {
  420. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  421. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8=");
  422. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  423. }
  424. Y_UNIT_TEST(TestDecodeTwoPaddingLongString) {
  425. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  426. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ==");
  427. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  428. }
  429. Y_UNIT_TEST(TestDecodeURLEncodedNoPaddingLongString) {
  430. const auto x = "How do I convert between big-endian and little-endian values in C++?a";
  431. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h");
  432. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  433. }
  434. Y_UNIT_TEST(TestDecodeURLEncodedOnePaddingLongString) {
  435. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  436. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8,");
  437. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  438. }
  439. Y_UNIT_TEST(TestDecodeURLEncodedTwoPaddingLongString) {
  440. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  441. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ,,");
  442. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  443. }
  444. Y_UNIT_TEST(TestDecodeUnevenDst) {
  445. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  446. TString b64 = "SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ";
  447. TVector<char> buf(Base64DecodeBufSize(b64.Size()), '\0');
  448. Base64DecodeUneven(buf.begin(), b64);
  449. TString res(buf.data());
  450. UNIT_ASSERT_VALUES_EQUAL(x, res);
  451. }
  452. Y_UNIT_TEST(TestDecodeUnevenDst2) {
  453. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  454. TString b64 = "SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8";
  455. TVector<char> buf(Base64DecodeBufSize(b64.Size()), '\0');
  456. Base64DecodeUneven(buf.begin(), b64);
  457. TString res(buf.data());
  458. UNIT_ASSERT_VALUES_EQUAL(x, res);
  459. }
  460. }