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