CppWAMP
C++11 client library for the WAMP protocol
base64.hpp
1 /*------------------------------------------------------------------------------
2  Copyright Butterfly Energy Systems 2014-2015, 2022.
3  Distributed under the Boost Software License, Version 1.0.
4  http://www.boost.org/LICENSE_1_0.txt
5 ------------------------------------------------------------------------------*/
6 
7 #ifndef CPPWAMP_BASE64_HPP
8 #define CPPWAMP_BASE64_HPP
9 
10 #include <array>
11 #include <cassert>
12 #include <cstdint>
13 #include <vector>
14 #include "../config.hpp"
15 #include "../erroror.hpp"
16 
17 namespace wamp
18 {
19 
20 namespace internal
21 {
22 
23 //------------------------------------------------------------------------------
24 class Base64
25 {
26 public:
27  template <typename TSink>
28  static void encode(const void* data, std::size_t size, TSink& sink)
29  {
30  Quad quad;
31  Byte sextet;
32  auto byte = static_cast<const Byte*>(data);
33  auto end = byte + size;
34  while (byte != end)
35  {
36  quad[0] = charFromSextet( (*byte >> 2) & 0x3f );
37  sextet = (*byte << 4) & 0x30;
38  ++byte;
39 
40  if (byte != end)
41  {
42  sextet |= (*byte >> 4) & 0x0f;
43  quad[1] = charFromSextet(sextet);
44  sextet = (*byte << 2) & 0x3c;
45  ++byte;
46 
47  if (byte != end)
48  {
49  sextet |= (*byte >> 6) & 0x03;
50  quad[2] = charFromSextet(sextet);
51  quad[3] = charFromSextet( *byte & 0x3f );
52  ++byte;
53  }
54  else
55  {
56  quad[2] = charFromSextet(sextet);
57  quad[3] = pad;
58  }
59  }
60  else
61  {
62  quad[1] = charFromSextet(sextet);
63  quad[2] = pad;
64  quad[3] = pad;
65  }
66 
67  using SinkByte = typename TSink::value_type;
68  auto ptr = reinterpret_cast<const SinkByte*>(quad.data());
69  sink.append(ptr, quad.size());
70  }
71  }
72 
73  template <typename TOutputByteContainer>
74  CPPWAMP_NODISCARD static std::error_code
75  decode(const void* data, size_t length, TOutputByteContainer& output)
76  {
77  if (length == 0)
78  return {};
79  if (length % 4 != 0)
80  return make_error_code(DecodingErrc::badBase64Length);
81 
82  ErrorOr<Triplet> triplet;
83  auto str = static_cast<const char*>(data);
84  const char* quad = str;
85  auto end = str + length - 4;
86  assert(end >= str);
87  for (; quad < end; quad += 4)
88  {
89  triplet = tripletFromQuad(quad, false);
90  if (!triplet)
91  return triplet.error();
92  append(*triplet, output);
93  }
94 
95  assert((quad + 4) == (str + length));
96 
97  unsigned lastTripletCount = 1;
98  triplet = tripletFromQuad(quad, true);
99  if (!triplet)
100  return triplet.error();
101  if (quad[0] == pad || quad[1] == pad)
102  return make_error_code(DecodingErrc::badBase64Padding);
103 
104  if (quad[2] == pad)
105  {
106  if (quad[3] != pad)
107  return make_error_code(DecodingErrc::badBase64Padding);
108  }
109  else
110  lastTripletCount = (quad[3] == pad) ? 2 : 3;
111 
112  triplet = tripletFromQuad(quad, true);
113  if (!triplet)
114  return triplet.error();
115  append(*triplet, lastTripletCount, output);
116 
117  return {};
118  }
119 
120 private:
121  using Byte = uint8_t;
122  using Triplet = std::array<Byte, 3>;
123  using Quad = std::array<char, 4>;
124 
125  static constexpr char pad = '=';
126 
127  static char charFromSextet(uint8_t sextet)
128  {
129  static const char alphabet[] =
130  {
131  'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0-7
132  'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8-15
133  'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16-23
134  'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24-31
135  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32-39
136  'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40-47
137  'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48-55
138  '4', '5', '6', '7', '8', '9', '+', '/' // 56-63
139  };
140  assert(sextet < sizeof(alphabet));
141  return alphabet[sextet];
142  }
143 
144  static ErrorOr<Triplet> tripletFromQuad(const char* quad, bool padAllowed)
145  {
146  std::array<Byte, 4> sextet;
147  for (unsigned i=0; i<4; ++i)
148  {
149  auto s = sextetFromChar(quad[i], padAllowed);
150  if (!s)
151  return makeUnexpected(s.error());
152  sextet[i] = s.value();
153  }
154  Triplet triplet;
155  triplet[0] = ((sextet[0] << 2) & 0xfc) | ((sextet[1] >> 4) & 0x03);
156  triplet[1] = ((sextet[1] << 4) & 0xf0) | ((sextet[2] >> 2) & 0x0f);
157  triplet[2] = ((sextet[2] << 6) & 0xc0) | ((sextet[3] ) & 0x3f);
158  return triplet;
159  }
160 
161  static ErrorOr<Byte> sextetFromChar(char c, bool padAllowed)
162  {
163  static const uint8_t table[] =
164  {
165  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 0-7
166  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 8-15
167  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 16-23
168  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 24-31
169  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 32-39
170 
171  // '+' '/'
172  0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f, // 40-47
173 
174  // '0' '1' '2' '3' '4' '5' '6' '7'
175  0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, // 48-55
176 
177  // '8' '9' '='
178  0x3c, 0x3d, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, // 56-63
179 
180  // 'A' 'B' 'C' 'D' 'E' 'F' 'G'
181  0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 64-71
182 
183  // 'H' 'I' 'J' 'K' 'L' 'M' 'N' 'O'
184  0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, // 72-79
185 
186  // 'P' 'Q' 'R' 'S' 'T' 'U' 'V' 'W'
187  0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 80-87
188 
189  // 'X' 'Y' 'Z'
190  0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff, // 88-95
191 
192  // 'a' 'b' 'c' 'd' 'e' 'f' 'g'
193  0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, // 96-103
194 
195  // 'h' 'i' 'j' 'k' 'l' 'm' 'n' 'o'
196  0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 104-111
197 
198  // 'p' 'q' 'r' 's' 't' 'u' 'v' 'w'
199  0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, // 112-119
200 
201  // 'x' 'y' 'z'
202  0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff, // 120-127
203 
204  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 128-135
205  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 136-143
206  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 144-151
207  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 152-159
208  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 160-167
209  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 168-175
210  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 176-183
211  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 184-191
212  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 192-199
213  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 200-207
214  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 208-215
215  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 216-223
216  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 224-231
217  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 232-239
218  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // 240-247
219  0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff // 248-255
220  };
221 
222  if (!padAllowed && c == pad)
224  uint8_t index = c;
225  uint8_t sextet = table[index];
226  if (sextet == 0xff)
228  assert(sextet < 64);
229  return sextet;
230  }
231 
232  template <typename TOutputByteContainer>
233  static void append(Triplet triplet, size_t length,
234  TOutputByteContainer& output)
235  {
236  using OutputByte = typename TOutputByteContainer::value_type;
237  auto data = reinterpret_cast<const OutputByte*>(triplet.data());
238  output.insert(output.end(), data, data + length);
239  }
240 
241  template <typename TOutputByteContainer>
242  static void append(Triplet triplet, TOutputByteContainer& output)
243  {
244  append(triplet, triplet.size(), output);
245  }
246 }; // class Base64
247 
248 } // namespace internal
249 
250 } // namespace wamp
251 
252 #endif // CPPWAMP_BASE64_HPP
253 
wamp::DecodingErrc::badBase64Char
@ badBase64Char
Invalid Base64 character.
wamp::DecodingErrc::badBase64Length
@ badBase64Length
Invalid Base64 string length.
wamp::encode
void encode(const Variant &variant, TOutput &&output)
Encodes the given variant to the given byte container or stream.
Definition: codec.hpp:103
wamp::DecodingErrc::badBase64Padding
@ badBase64Padding
Invalid Base64 padding.
wamp::makeUnexpectedError
UnexpectedError makeUnexpectedError(TErrorEnum errc)
Convenience function that creates an UnexpectedError from an error code enum.
Definition: erroror.hpp:113
wamp
Definition: anyhandler.hpp:36
wamp::decode
std::error_code decode(TInput &&input, Variant &variant)
Decodes from the given byte sequence or stream to the given variant.
Definition: codec.hpp:172