openMSX
Base64.cc
Go to the documentation of this file.
1 #include "Base64.hh"
2 #include "likely.hh"
3 #include "xrange.hh"
4 #include <algorithm>
5 #include <cassert>
6 
7 namespace Base64 {
8 
9 using std::string;
10 using openmsx::MemBuffer;
11 
12 [[nodiscard]] static constexpr char encode(uint8_t c)
13 {
14  constexpr const char* const base64_chars =
15  "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
16  "abcdefghijklmnopqrstuvwxyz"
17  "0123456789+/";
18  assert(c < 64);
19  return base64_chars[c];
20 }
21 
22 [[nodiscard]] static constexpr uint8_t decode(uint8_t c)
23 {
24  if ('A' <= c && c <= 'Z') {
25  return c - 'A';
26  } else if ('a' <= c && c <= 'z') {
27  return c - 'a' + 26;
28  } else if ('0' <= c && c <= '9') {
29  return c - '0' + 52;
30  } else if (c == '+') {
31  return 62;
32  } else if (c == '/') {
33  return 63;
34  } else {
35  return uint8_t(-1);
36  }
37 }
38 
39 string encode(const uint8_t* input, size_t inSize)
40 {
41  constexpr int CHUNKS = 19;
42  constexpr int IN_CHUNKS = 3 * CHUNKS;
43  constexpr int OUT_CHUNKS = 4 * CHUNKS; // 76 chars per line
44 
45  auto outSize = ((inSize + (IN_CHUNKS - 1)) / IN_CHUNKS) * (OUT_CHUNKS + 1); // overestimation
46  string ret(outSize, 0); // too big
47 
48  size_t out = 0;
49  while (inSize) {
50  if (out) ret[out++] = '\n';
51  auto n2 = std::min<size_t>(IN_CHUNKS, inSize);
52  auto n = unsigned(n2);
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 += 3;
61  }
62  if (n) {
63  uint8_t buf3[3] = { 0, 0, 0 };
64  for (auto i : xrange(n)) {
65  buf3[i] = input[i];
66  }
67  uint8_t buf4[4];
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  inSize -= n2;
82  }
83 
84  assert(outSize >= out);
85  ret.resize(out); // shrink to correct size
86  return ret;
87 }
88 
89 std::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  uint8_t buf4[4];
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  uint8_t buf3[3];
113  buf3[0] = ((buf4[0] & 0xff) << 2) + ((buf4[1] & 0x30) >> 4);
114  buf3[1] = ((buf4[1] & 0x0f) << 4) + ((buf4[2] & 0x3c) >> 2);
115  buf3[2] = ((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::pair(std::move(ret), out);
124 }
125 
126 bool decode_inplace(std::string_view input, uint8_t* output, size_t outSize)
127 {
128  unsigned i = 0;
129  size_t out = 0;
130  uint8_t buf4[4];
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 (unlikely((out + 3) > outSize)) 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  uint8_t buf3[3];
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 (unlikely(out == outSize)) 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
#define unlikely(x)
Definition: likely.hh:15
Definition: Base64.cc:7
bool decode_inplace(std::string_view input, uint8_t *output, size_t outSize)
Definition: Base64.cc:126
constexpr auto xrange(T e)
Definition: xrange.hh:155