openMSX
Base64.cc
Go to the documentation of this file.
1#include "Base64.hh"
2#include "narrow.hh"
3#include "ranges.hh"
4#include "xrange.hh"
5#include <algorithm>
6#include <array>
7#include <cassert>
8
9namespace Base64 {
10
12
13[[nodiscard]] static constexpr char encode(uint8_t c)
14{
15 constexpr std::array<char, 64> base64_chars = {
16 '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',
17 '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',
18 '0','1','2','3','4','5','6','7','8','9','+','/',
19 };
20 assert(c < 64);
21 return base64_chars[c];
22}
23
24[[nodiscard]] static constexpr uint8_t decode(uint8_t c)
25{
26 if ('A' <= c && c <= 'Z') {
27 return c - 'A';
28 } else if ('a' <= c && c <= 'z') {
29 return c - 'a' + 26;
30 } else if ('0' <= c && c <= '9') {
31 return c - '0' + 52;
32 } else if (c == '+') {
33 return 62;
34 } else if (c == '/') {
35 return 63;
36 } else {
37 return uint8_t(-1);
38 }
39}
40
41std::string encode(std::span<const uint8_t> input)
42{
43 constexpr int CHUNKS = 19;
44 constexpr int IN_CHUNKS = 3 * CHUNKS;
45 constexpr int OUT_CHUNKS = 4 * CHUNKS; // 76 chars per line
46
47 auto outSize = ((input.size() + (IN_CHUNKS - 1)) / IN_CHUNKS) * (OUT_CHUNKS + 1); // overestimation
48 std::string ret(outSize, 0); // too big
49
50 size_t out = 0;
51 while (!input.empty()) {
52 if (out) ret[out++] = '\n';
53 auto n = std::min<size_t>(IN_CHUNKS, input.size());
54 for (; n >= 3; n -= 3) {
55 ret[out++] = encode(uint8_t( (input[0] & 0xfc) >> 2));
56 ret[out++] = encode(uint8_t(((input[0] & 0x03) << 4) +
57 ((input[1] & 0xf0) >> 4)));
58 ret[out++] = encode(uint8_t(((input[1] & 0x0f) << 2) +
59 ((input[2] & 0xc0) >> 6)));
60 ret[out++] = encode(uint8_t( (input[2] & 0x3f) >> 0));
61 input = input.subspan(3);
62 }
63 if (n) {
64 std::array<uint8_t, 3> buf3 = {0, 0, 0};
65 ranges::copy(input.subspan(0, n), buf3);
66 input = input.subspan(n);
67
68 std::array<uint8_t, 4> buf4;
69 buf4[0] = uint8_t( (buf3[0] & 0xfc) >> 2);
70 buf4[1] = uint8_t(((buf3[0] & 0x03) << 4) +
71 ((buf3[1] & 0xf0) >> 4));
72 buf4[2] = uint8_t(((buf3[1] & 0x0f) << 2) +
73 ((buf3[2] & 0xc0) >> 6));
74 buf4[3] = uint8_t( (buf3[2] & 0x3f) >> 0);
75 for (auto j : xrange(n + 1)) {
76 ret[out++] = encode(buf4[j]);
77 }
78 for (; n < 3; ++n) {
79 ret[out++] = '=';
80 }
81 }
82 }
83
84 assert(outSize >= out);
85 ret.resize(out); // shrink to correct size
86 return ret;
87}
88
89std::pair<MemBuffer<uint8_t>, size_t> decode(std::string_view input)
90{
91 auto outSize = (input.size() * 3 + 3) / 4; // overestimation
92 MemBuffer<uint8_t> ret(outSize); // too big
93
94 unsigned i = 0;
95 size_t out = 0;
96 std::array<uint8_t, 4> buf4;
97 for (auto c : input) {
98 uint8_t d = decode(c);
99 if (d == uint8_t(-1)) continue;
100 buf4[i++] = d;
101 if (i == 4) {
102 i = 0;
103 ret[out++] = char(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4));
104 ret[out++] = char(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2));
105 ret[out++] = char(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0));
106 }
107 }
108 if (i) {
109 for (auto j : xrange(i, 4u)) {
110 buf4[j] = 0;
111 }
112 std::array<uint8_t, 3> buf3;
113 buf3[0] = narrow_cast<uint8_t>(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4));
114 buf3[1] = narrow_cast<uint8_t>(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2));
115 buf3[2] = narrow_cast<uint8_t>(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0));
116 for (auto j : xrange(i - 1)) {
117 ret[out++] = buf3[j];
118 }
119 }
120
121 assert(outSize >= out);
122 ret.resize(out); // shrink to correct size
123 return {std::move(ret), out};
124}
125
126bool decode_inplace(std::string_view input, std::span<uint8_t> output)
127{
128 auto outSize = output.size();
129 unsigned i = 0;
130 size_t out = 0;
131 std::array<uint8_t, 4> buf4;
132 for (auto c : input) {
133 uint8_t d = decode(c);
134 if (d == uint8_t(-1)) continue;
135 buf4[i++] = d;
136 if (i == 4) {
137 i = 0;
138 if ((out + 3) > outSize) [[unlikely]] return false;
139 output[out++] = char(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4));
140 output[out++] = char(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2));
141 output[out++] = char(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0));
142 }
143 }
144 if (i) {
145 for (auto j : xrange(i, 4u)) {
146 buf4[j] = 0;
147 }
148 std::array<uint8_t, 3> buf3;
149 buf3[0] = narrow_cast<uint8_t>(((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4));
150 buf3[1] = narrow_cast<uint8_t>(((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2));
151 buf3[2] = narrow_cast<uint8_t>(((buf4[2] & 0x03) << 6) + ((buf4[3] & 0xff) >> 0));
152 for (auto j : xrange(i - 1)) {
153 if (out == outSize) [[unlikely]] return false;
154 output[out++] = buf3[j];
155 }
156 }
157
158 return out == outSize;
159}
160
161} // namespace Base64
This class manages the lifetime of a block of memory.
Definition MemBuffer.hh:29
void resize(size_t size)
Grow or shrink the memory block.
Definition MemBuffer.hh:111
bool decode_inplace(std::string_view input, std::span< uint8_t > output)
Definition Base64.cc:126
auto copy(InputRange &&range, OutputIter out)
Definition ranges.hh:250
constexpr auto xrange(T e)
Definition xrange.hh:132