base64_ut.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554
  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& encodedNoPadding, 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. Base64EncodeNoPadding(plain, a);
  160. UNIT_ASSERT_VALUES_EQUAL(a, encodedNoPadding);
  161. TString c = Base64DecodeUneven(a);
  162. UNIT_ASSERT_VALUES_EQUAL(c, plain);
  163. Base64EncodeUrlNoPadding(plain, a);
  164. UNIT_ASSERT_VALUES_EQUAL(a, encodedUrlNoPadding);
  165. TString d = Base64DecodeUneven(a);
  166. UNIT_ASSERT_VALUES_EQUAL(d, plain);
  167. }
  168. static void TestEncodeStrictDecodeIntoString(const TString& plain, const TString& encoded, const TString& encodedUrl) {
  169. TString a, b;
  170. Base64Encode(plain, a);
  171. UNIT_ASSERT_VALUES_EQUAL(a, encoded);
  172. Base64StrictDecode(a, b);
  173. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  174. Base64EncodeUrl(plain, a);
  175. UNIT_ASSERT_VALUES_EQUAL(a, encodedUrl);
  176. Base64StrictDecode(a, b);
  177. UNIT_ASSERT_VALUES_EQUAL(b, plain);
  178. }
  179. Y_UNIT_TEST_SUITE(TBase64) {
  180. Y_UNIT_TEST(TestEncode) {
  181. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12z"), "MTJ6");
  182. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("123"), "MTIz");
  183. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("12"), "MTI=");
  184. UNIT_ASSERT_VALUES_EQUAL(Base64Encode("1"), "MQ==");
  185. }
  186. Y_UNIT_TEST(TestIntoString) {
  187. {
  188. TString str;
  189. for (size_t i = 0; i < 256; ++i)
  190. str += char(i);
  191. const TString base64 =
  192. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  193. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9Q"
  194. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  195. "l6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  196. "oqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIyc"
  197. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy"
  198. "8/T19vf4+fr7/P3+/w==";
  199. const TString base64Url =
  200. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  201. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9Q"
  202. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  203. "l6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  204. "oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIyc"
  205. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy"
  206. "8_T19vf4-fr7_P3-_w,,";
  207. const TString base64WithoutPadding =
  208. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  209. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9Q"
  210. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  211. "l6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  212. "oqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIyc"
  213. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy"
  214. "8/T19vf4+fr7/P3+/w";
  215. const TString base64UrlWithoutPadding =
  216. "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJy"
  217. "gpKissLS4vMDEyMzQ1Njc4OTo7PD0-P0BBQkNERUZHSElKS0xNTk9Q"
  218. "UVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eH"
  219. "l6e3x9fn-AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6Ch"
  220. "oqOkpaanqKmqq6ytrq-wsbKztLW2t7i5uru8vb6_wMHCw8TFxsfIyc"
  221. "rLzM3Oz9DR0tPU1dbX2Nna29zd3t_g4eLj5OXm5-jp6uvs7e7v8PHy"
  222. "8_T19vf4-fr7_P3-_w";
  223. TestEncodeDecodeIntoString(str, base64, base64Url, base64WithoutPadding, base64UrlWithoutPadding);
  224. TestEncodeStrictDecodeIntoString(str, base64, base64Url);
  225. }
  226. {
  227. const TString str = "http://yandex.ru:1234/request?param=value&lll=fff#fragment";
  228. const TString base64 = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q/cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA==";
  229. const TString base64Url = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q_cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA,,";
  230. const TString base64WithoutPadding = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q/cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA";
  231. const TString base64UrlWithoutPadding = "aHR0cDovL3lhbmRleC5ydToxMjM0L3JlcXVlc3Q_cGFyYW09dmFsdWUmbGxsPWZmZiNmcmFnbWVudA";
  232. TestEncodeDecodeIntoString(str, base64, base64Url, base64WithoutPadding, base64UrlWithoutPadding);
  233. TestEncodeStrictDecodeIntoString(str, base64, base64Url);
  234. }
  235. }
  236. Y_UNIT_TEST(TestDecode) {
  237. UNIT_ASSERT_EXCEPTION(Base64Decode("a"), yexception);
  238. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("a"), yexception);
  239. UNIT_ASSERT_VALUES_EQUAL(Base64Decode(""), "");
  240. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(""), "");
  241. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("MTI="), "12");
  242. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("MTI="), "12");
  243. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("QQ=="), "A");
  244. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("QQ=="), "A");
  245. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("M=I="), yexception);
  246. UNIT_ASSERT_VALUES_EQUAL(Base64Decode("dnluZHg="), "vyndx");
  247. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg="), "vyndx");
  248. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode("dnluZHg=dmlkZW8="), "vyndxvideo");
  249. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHR0cDovL2ltZy5tZWdhLXBvcm5vLnJ1Lw=a"), yexception);
  250. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("aHh=="), yexception);
  251. UNIT_ASSERT_EXCEPTION(Base64StrictDecode("\1\1\1\2"), yexception);
  252. }
  253. Y_UNIT_TEST(TestDecodeUneven) {
  254. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven(""), "");
  255. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("YWFh"), "aaa");
  256. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI="), "12");
  257. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI,"), "12");
  258. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("MTI"), "12");
  259. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ=="), "A");
  260. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ,,"), "A");
  261. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("QQ"), "A");
  262. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg="), "vyndx");
  263. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg,"), "vyndx");
  264. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven("dnluZHg"), "vyndx");
  265. }
  266. Y_UNIT_TEST(TestDecodeRandom) {
  267. TString input;
  268. constexpr size_t testSize = 240000;
  269. for (size_t i = 0; i < testSize; ++i) {
  270. input.push_back(rand() % 256);
  271. }
  272. TString output;
  273. TString encoded = Base64Encode(input);
  274. TString encodedUrl = TString::Uninitialized(Base64EncodeBufSize(input.length()));
  275. Base64EncodeUrlNoPadding(input, encodedUrl);
  276. UNIT_ASSERT_VALUES_EQUAL(Base64Decode(encoded), input);
  277. UNIT_ASSERT_VALUES_EQUAL(Base64StrictDecode(encoded), input);
  278. UNIT_ASSERT_VALUES_EQUAL(Base64DecodeUneven(encodedUrl), input);
  279. }
  280. Y_UNIT_TEST(TestAllPossibleOctets) {
  281. 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);
  282. const TString xEnc = "AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn8=";
  283. const TString y = Base64Decode(xEnc);
  284. const TString yEnc = Base64Encode(x);
  285. UNIT_ASSERT_VALUES_EQUAL(x, y);
  286. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  287. }
  288. Y_UNIT_TEST(TestTwoPaddingCharacters) {
  289. const TString x("a");
  290. const TString xEnc = "YQ==";
  291. const TString y = Base64Decode(xEnc);
  292. const TString yEnc = Base64Encode(x);
  293. UNIT_ASSERT_VALUES_EQUAL(x, y);
  294. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  295. }
  296. Y_UNIT_TEST(TestOnePaddingCharacter) {
  297. const TString x("aa");
  298. const TString xEnc = "YWE=";
  299. const TString y = Base64Decode(xEnc);
  300. const TString yEnc = Base64Encode(x);
  301. UNIT_ASSERT_VALUES_EQUAL(x, y);
  302. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  303. }
  304. Y_UNIT_TEST(TestNoPaddingCharacters) {
  305. const TString x("aaa");
  306. const TString xEnc = "YWFh";
  307. const TString y = Base64Decode(xEnc);
  308. const TString yEnc = Base64Encode(x);
  309. UNIT_ASSERT_VALUES_EQUAL(x, y);
  310. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  311. }
  312. Y_UNIT_TEST(TestTrailingZero) {
  313. const TString x("foo\0"sv);
  314. const TString xEnc = "Zm9vAA==";
  315. const TString y = Base64Decode(xEnc);
  316. const TString yEnc = Base64Encode(x);
  317. UNIT_ASSERT_VALUES_EQUAL(x, y);
  318. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  319. }
  320. Y_UNIT_TEST(TestTwoTrailingZeroes) {
  321. const TString x("foo\0\0"sv);
  322. const TString xEnc = "Zm9vAAA=";
  323. const TString y = Base64Decode(xEnc);
  324. const TString yEnc = Base64Encode(x);
  325. UNIT_ASSERT_VALUES_EQUAL(x, y);
  326. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  327. }
  328. Y_UNIT_TEST(TestZero) {
  329. const TString x("\0"sv);
  330. const TString xEnc = "AA==";
  331. const TString y = Base64Decode(xEnc);
  332. const TString yEnc = Base64Encode(x);
  333. UNIT_ASSERT_VALUES_EQUAL(x, y);
  334. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  335. }
  336. Y_UNIT_TEST(TestSymbolsAfterZero) {
  337. const TString x("\0a"sv);
  338. const TString xEnc = "AGE=";
  339. const TString y = Base64Decode(xEnc);
  340. const TString yEnc = Base64Encode(x);
  341. UNIT_ASSERT_VALUES_EQUAL(x, y);
  342. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  343. }
  344. Y_UNIT_TEST(TestEmptyString) {
  345. const TString x = "";
  346. const TString xEnc = "";
  347. const TString y = Base64Decode(xEnc);
  348. const TString yEnc = Base64Encode(x);
  349. UNIT_ASSERT_VALUES_EQUAL(x, y);
  350. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  351. }
  352. Y_UNIT_TEST(TestBackendsConsistencyOnRandomData) {
  353. constexpr size_t TEST_CASES_COUNT = 1000;
  354. constexpr size_t MAX_DATA_SIZE = 1000;
  355. TFastRng<ui32> prng{42};
  356. TVector<TString> xs{TEST_CASES_COUNT};
  357. TString xEnc;
  358. TString xDec;
  359. TString yEnc;
  360. TString yDec;
  361. for (auto& x : xs) {
  362. const size_t size = prng() % MAX_DATA_SIZE;
  363. for (size_t j = 0; j < size; ++j) {
  364. x += static_cast<char>(prng() % 256);
  365. }
  366. }
  367. static const auto IMPLS = NB64Etalon::GetImpls();
  368. for (size_t i = 0; i < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++i) {
  369. for (size_t j = 0; j < static_cast<size_t>(NB64Etalon::TImpls::MAX_IMPL); ++j) {
  370. const auto ei = static_cast<NB64Etalon::TImpls::EImpl>(i);
  371. const auto ej = static_cast<NB64Etalon::TImpls::EImpl>(j);
  372. const auto impl = IMPLS.Impl[i];
  373. const auto otherImpl = IMPLS.Impl[j];
  374. if (!impl.Encode && !impl.Decode || !otherImpl.Encode && !otherImpl.Decode) {
  375. continue;
  376. }
  377. for (const auto& x : xs) {
  378. impl.Encode(x, xEnc);
  379. impl.Decode(xEnc, xDec);
  380. Y_ENSURE(x == xDec, "something is wrong with " << ei << " implementation");
  381. otherImpl.Encode(x, yEnc);
  382. otherImpl.Decode(xEnc, yDec);
  383. Y_ENSURE(x == yDec, "something is wrong with " << ej << " implementation");
  384. UNIT_ASSERT_VALUES_EQUAL(xEnc, yEnc);
  385. UNIT_ASSERT_VALUES_EQUAL(xDec, yDec);
  386. }
  387. }
  388. }
  389. }
  390. Y_UNIT_TEST(TestIfEncodedDataIsZeroTerminatedOnRandomData) {
  391. constexpr size_t TEST_CASES_COUNT = 1000;
  392. constexpr size_t MAX_DATA_SIZE = 1000;
  393. TFastRng<ui32> prng{42};
  394. TString x;
  395. TVector<char> buf;
  396. for (size_t i = 0; i < TEST_CASES_COUNT; ++i) {
  397. const size_t size = prng() % MAX_DATA_SIZE;
  398. x.clear();
  399. for (size_t j = 0; j < size; ++j) {
  400. x += static_cast<char>(prng() % 256);
  401. }
  402. buf.assign(Base64EncodeBufSize(x.size()), Max<char>());
  403. const auto* const xEncEnd = Base64Encode(buf.data(), (const unsigned char*)x.data(), x.size());
  404. UNIT_ASSERT_VALUES_EQUAL(*xEncEnd, '\0');
  405. }
  406. }
  407. Y_UNIT_TEST(TestDecodeURLEncodedNoPadding) {
  408. const auto x = "123";
  409. const auto xDec = Base64Decode("MTIz");
  410. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  411. }
  412. Y_UNIT_TEST(TestDecodeURLEncodedOnePadding) {
  413. const auto x = "12";
  414. const auto xDec = Base64Decode("MTI,");
  415. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  416. }
  417. Y_UNIT_TEST(TestDecodeURLEncodedTwoPadding) {
  418. const auto x = "1";
  419. const auto xDec = Base64Decode("MQ,,");
  420. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  421. }
  422. Y_UNIT_TEST(TestDecodeURLEncodedWithoutPadding) {
  423. const auto x = "1";
  424. const auto xDec = Base64DecodeUneven("MQ");
  425. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  426. }
  427. Y_UNIT_TEST(TestDecodeNoPaddingLongString) {
  428. const auto x = "How do I convert between big-endian and little-endian values in C++?a";
  429. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h");
  430. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  431. }
  432. Y_UNIT_TEST(TestDecodeOnePaddingLongString) {
  433. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  434. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8=");
  435. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  436. }
  437. Y_UNIT_TEST(TestDecodeTwoPaddingLongString) {
  438. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  439. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ==");
  440. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  441. }
  442. Y_UNIT_TEST(TestDecodeURLEncodedNoPaddingLongString) {
  443. const auto x = "How do I convert between big-endian and little-endian values in C++?a";
  444. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9h");
  445. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  446. }
  447. Y_UNIT_TEST(TestDecodeURLEncodedOnePaddingLongString) {
  448. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  449. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8,");
  450. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  451. }
  452. Y_UNIT_TEST(TestDecodeURLEncodedTwoPaddingLongString) {
  453. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  454. const auto xDec = Base64Decode("SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ,,");
  455. UNIT_ASSERT_VALUES_EQUAL(x, xDec);
  456. }
  457. Y_UNIT_TEST(TestDecodeUnevenDst) {
  458. const auto x = "How do I convert between big-endian and little-endian values in C++?aa";
  459. TString b64 = "SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz9hYQ";
  460. TVector<char> buf(Base64DecodeBufSize(b64.Size()), '\0');
  461. Base64DecodeUneven(buf.begin(), b64);
  462. TString res(buf.data());
  463. UNIT_ASSERT_VALUES_EQUAL(x, res);
  464. }
  465. Y_UNIT_TEST(TestDecodeUnevenDst2) {
  466. const auto x = "How do I convert between big-endian and little-endian values in C++?";
  467. TString b64 = "SG93IGRvIEkgY29udmVydCBiZXR3ZWVuIGJpZy1lbmRpYW4gYW5kIGxpdHRsZS1lbmRpYW4gdmFsdWVzIGluIEMrKz8";
  468. TVector<char> buf(Base64DecodeBufSize(b64.Size()), '\0');
  469. Base64DecodeUneven(buf.begin(), b64);
  470. TString res(buf.data());
  471. UNIT_ASSERT_VALUES_EQUAL(x, res);
  472. }
  473. }