| 1 |
#include "Base64.hh" |
| 2 |
|
| 3 |
#include "narrow.hh" |
| 4 |
#include "ranges.hh" |
| 5 |
#include "xrange.hh" |
| 6 |
|
| 7 |
#include <algorithm> |
| 8 |
#include <array> |
| 9 |
#include <cassert> |
| 10 |
|
| 11 |
namespace Base64 { |
| 12 |
|
| 13 |
using openmsx::MemBuffer; |
| 14 |
|
| 15 |
[[nodiscard]] static constexpr char encode(uint8_t c) |
| 16 |
{ |
| 17 |
constexpr std::array<char, 64> base64_chars = { |
| 18 |
'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z', |
| 19 |
'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z', |
| 20 |
'0','1','2','3','4','5','6','7','8','9','+','/', |
| 21 |
}; |
| 22 |
assert(c < 64); |
| 23 |
return base64_chars[c]; |
| 24 |
} |
| 25 |
|
| 26 |
[[nodiscard]] static constexpr uint8_t decode(uint8_t c) |
| 27 |
{ |
| 28 |
if ('A' <= c && c <= 'Z') { |
| 29 |
return c - 'A'; |
| 30 |
} else if ('a' <= c && c <= 'z') { |
| 31 |
return c - 'a' + 26; |
| 32 |
} else if ('0' <= c && c <= '9') { |
| 33 |
return c - '0' + 52; |
| 34 |
} else if (c == '+') { |
| 35 |
return 62; |
| 36 |
} else if (c == '/') { |
| 37 |
return 63; |
| 38 |
} else { |
| 39 |
return uint8_t(-1); |
| 40 |
} |
| 41 |
} |
| 42 |
|
| 43 |
std::string encode(std::span<const uint8_t> input) |
| 44 |
{ |
| 45 |
constexpr int CHUNKS = 19; |
| 46 |
constexpr int IN_CHUNKS = 3 * CHUNKS; |
| 47 |
constexpr int OUT_CHUNKS = 4 * CHUNKS; |
| 48 |
|
| 49 |
auto outSize = ((input.size() + (IN_CHUNKS - 1)) / IN_CHUNKS) * (OUT_CHUNKS + 1); |
| 50 |
std::string ret; |
| 51 |
ret.resize_and_overwrite(outSize, [&](char* buf, size_t) { |
| 52 |
size_t out = 0; |
| 53 |
while (!input.empty()) { |
| 54 |
if (out) buf[out++] = '\n'; |
| 55 |
auto n = std::min<size_t>(IN_CHUNKS, input.size()); |
| 56 |
for (; n >= 3; n -= 3) { |
| 57 |
buf[out++] = encode(uint8_t( (input[0] & 0xfc) >> 2)); |
| 58 |
buf[out++] = encode(uint8_t(((input[0] & 0x03) << 4) + |
| 59 |
((input[1] & 0xf0) >> 4))); |
| 60 |
buf[out++] = encode(uint8_t(((input[1] & 0x0f) << 2) + |
| 61 |
((input[2] & 0xc0) >> 6))); |
| 62 |
buf[out++] = encode(uint8_t( (input[2] & 0x3f) >> 0)); |
| 63 |
input = input.subspan(3); |
| 64 |
} |
| 65 |
if (n) { |
| 66 |
std::array<uint8_t, 3> buf3 = {0, 0, 0}; |
| 67 |
copy_to_range(input.subspan(0, n), buf3); |
| 68 |
input = input.subspan(n); |
| 69 |
|
| 70 |
std::array<uint8_t, 4> buf4; |
| 71 |
buf4[0] = uint8_t( (buf3[0] & 0xfc) >> 2); |
| 72 |
buf4[1] = uint8_t(((buf3[0] & 0x03) << 4) + |
| 73 |
((buf3[1] & 0xf0) >> 4)); |
| 74 |
buf4[2] = uint8_t(((buf3[1] & 0x0f) << 2) + |
| 75 |
((buf3[2] & 0xc0) >> 6)); |
| 76 |
buf4[3] = uint8_t( (buf3[2] & 0x3f) >> 0); |
| 77 |
for (auto j : xrange(n + 1)) { |
| 78 |
buf[out++] = encode(buf4[j]); |
| 79 |
} |
| 80 |
for (; n < 3; ++n) { |
| 81 |
buf[out++] = '='; |
| 82 |
} |
| 83 |
} |
| 84 |
} |
| 85 |
|
| 86 |
assert(outSize >= out); |
| 87 |
return out; |
| 88 |
}); |
| 89 |
return ret; |
| 90 |
} |
| 91 |
|
| 92 |
MemBuffer<uint8_t> decode(std::string_view input) |
| 93 |
{ |
| 94 |
auto outSize = (input.size() * 3 + 3) / 4; |
| 95 |
MemBuffer<uint8_t> ret(outSize); |
| 96 |
|
| 97 |
unsigned i = 0; |
| 98 |
size_t out = 0; |
| 99 |
std::array<uint8_t, 4> buf4; |
| 100 |
for (auto c : input) { |
| 101 |
uint8_t d = decode(c); |
| 102 |
if (d == uint8_t(-1)) continue; |
| 103 |
buf4[i++] = d; |
| 104 |
if (i == 4) { |
| 105 |
i = 0; |
| 106 |
ret[out++] = char(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4)); |
| 107 |
ret[out++] = char(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2)); |
| 108 |
ret[out++] = char(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0)); |
| 109 |
} |
| 110 |
} |
| 111 |
if (i) { |
| 112 |
for (auto j : xrange(i, 4u)) { |
| 113 |
buf4[j] = 0; |
| 114 |
} |
| 115 |
std::array<uint8_t, 3> buf3; |
| 116 |
buf3[0] = narrow_cast<uint8_t>(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4)); |
| 117 |
buf3[1] = narrow_cast<uint8_t>(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2)); |
| 118 |
buf3[2] = narrow_cast<uint8_t>(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0)); |
| 119 |
for (auto j : xrange(i - 1)) { |
| 120 |
ret[out++] = buf3[j]; |
| 121 |
} |
| 122 |
} |
| 123 |
|
| 124 |
assert(outSize >= out); |
| 125 |
ret.resize(out); |
| 126 |
return ret; |
| 127 |
} |
| 128 |
|
| 129 |
bool decode_inplace(std::string_view input, std::span<uint8_t> output) |
| 130 |
{ |
| 131 |
auto outSize = output.size(); |
| 132 |
unsigned i = 0; |
| 133 |
size_t out = 0; |
| 134 |
std::array<uint8_t, 4> buf4; |
| 135 |
for (auto c : input) { |
| 136 |
uint8_t d = decode(c); |
| 137 |
if (d == uint8_t(-1)) continue; |
| 138 |
buf4[i++] = d; |
| 139 |
if (i == 4) { |
| 140 |
i = 0; |
| 141 |
if ((out + 3) > outSize) [[unlikely]] return false; |
| 142 |
output[out++] = char(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4)); |
| 143 |
output[out++] = char(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2)); |
| 144 |
output[out++] = char(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0)); |
| 145 |
} |
| 146 |
} |
| 147 |
if (i) { |
| 148 |
for (auto j : xrange(i, 4u)) { |
| 149 |
buf4[j] = 0; |
| 150 |
} |
| 151 |
std::array<uint8_t, 3> buf3; |
| 152 |
buf3[0] = narrow_cast<uint8_t>(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4)); |
| 153 |
buf3[1] = narrow_cast<uint8_t>(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2)); |
| 154 |
buf3[2] = narrow_cast<uint8_t>(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0)); |
| 155 |
for (auto j : xrange(i - 1)) { |
| 156 |
if (out == outSize) [[unlikely]] return false; |
| 157 |
output[out++] = buf3[j]; |
| 158 |
} |
| 159 |
} |
| 160 |
|
| 161 |
return out == outSize; |
| 162 |
} |
| 163 |
|
| 164 |
} |