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