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