Base64.cpp 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Array.h>
  7. #include <AK/Base64.h>
  8. #include <AK/StringBuilder.h>
  9. #include <AK/Types.h>
  10. #include <AK/Vector.h>
  11. namespace AK {
  12. static constexpr auto make_alphabet()
  13. {
  14. Array alphabet = {
  15. 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
  16. 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
  17. 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
  18. 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
  19. 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
  20. 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
  21. 'w', 'x', 'y', 'z', '0', '1', '2', '3',
  22. '4', '5', '6', '7', '8', '9', '+', '/'
  23. };
  24. return alphabet;
  25. }
  26. static constexpr auto make_lookup_table()
  27. {
  28. constexpr auto alphabet = make_alphabet();
  29. Array<i16, 256> table;
  30. table.fill(-1);
  31. for (size_t i = 0; i < alphabet.size(); ++i) {
  32. table[alphabet[i]] = i;
  33. }
  34. return table;
  35. }
  36. size_t calculate_base64_decoded_length(StringView input)
  37. {
  38. return input.length() * 3 / 4;
  39. }
  40. size_t calculate_base64_encoded_length(ReadonlyBytes input)
  41. {
  42. return ((4 * input.size() / 3) + 3) & ~3;
  43. }
  44. Optional<ByteBuffer> decode_base64(StringView input)
  45. {
  46. auto get = [&](const size_t offset, bool* is_padding) -> Optional<u8> {
  47. constexpr auto table = make_lookup_table();
  48. if (offset >= input.length())
  49. return 0;
  50. if (input[offset] == '=') {
  51. if (!is_padding)
  52. return {};
  53. *is_padding = true;
  54. return 0;
  55. }
  56. i16 result = table[static_cast<unsigned char>(input[offset])];
  57. if (result < 0)
  58. return {};
  59. VERIFY(result < 256);
  60. return { result };
  61. };
  62. #define TRY_GET(index, is_padding) \
  63. ({ \
  64. auto _temporary_result = get(index, is_padding); \
  65. if (!_temporary_result.has_value()) \
  66. return {}; \
  67. _temporary_result.value(); \
  68. })
  69. Vector<u8> output;
  70. output.ensure_capacity(calculate_base64_decoded_length(input));
  71. for (size_t i = 0; i < input.length(); i += 4) {
  72. bool in2_is_padding = false;
  73. bool in3_is_padding = false;
  74. const u8 in0 = TRY_GET(i, nullptr);
  75. const u8 in1 = TRY_GET(i + 1, nullptr);
  76. const u8 in2 = TRY_GET(i + 2, &in2_is_padding);
  77. const u8 in3 = TRY_GET(i + 3, &in3_is_padding);
  78. const u8 out0 = (in0 << 2) | ((in1 >> 4) & 3);
  79. const u8 out1 = ((in1 & 0xf) << 4) | ((in2 >> 2) & 0xf);
  80. const u8 out2 = ((in2 & 0x3) << 6) | in3;
  81. output.append(out0);
  82. if (!in2_is_padding)
  83. output.append(out1);
  84. if (!in3_is_padding)
  85. output.append(out2);
  86. }
  87. return ByteBuffer::copy(output);
  88. }
  89. String encode_base64(ReadonlyBytes input)
  90. {
  91. constexpr auto alphabet = make_alphabet();
  92. StringBuilder output(calculate_base64_encoded_length(input));
  93. auto get = [&](const size_t offset, bool* need_padding = nullptr) -> u8 {
  94. if (offset >= input.size()) {
  95. if (need_padding)
  96. *need_padding = true;
  97. return 0;
  98. }
  99. return input[offset];
  100. };
  101. for (size_t i = 0; i < input.size(); i += 3) {
  102. bool is_8bit = false;
  103. bool is_16bit = false;
  104. const u8 in0 = get(i);
  105. const u8 in1 = get(i + 1, &is_16bit);
  106. const u8 in2 = get(i + 2, &is_8bit);
  107. const u8 index0 = (in0 >> 2) & 0x3f;
  108. const u8 index1 = ((in0 << 4) | (in1 >> 4)) & 0x3f;
  109. const u8 index2 = ((in1 << 2) | (in2 >> 6)) & 0x3f;
  110. const u8 index3 = in2 & 0x3f;
  111. const u8 out0 = alphabet[index0];
  112. const u8 out1 = alphabet[index1];
  113. const u8 out2 = is_16bit ? '=' : alphabet[index2];
  114. const u8 out3 = is_8bit ? '=' : alphabet[index3];
  115. output.append(out0);
  116. output.append(out1);
  117. output.append(out2);
  118. output.append(out3);
  119. }
  120. return output.to_string();
  121. }
  122. }