rapidyaml  0.16.0
parseandemitYAML,anddoitfast
base64.cpp
Go to the documentation of this file.
1 #ifndef C4_BASE64_HPP_
2 #include "c4/base64.hpp"
3 #endif
4 #ifndef C4_ERROR_HPP_
5 #include "c4/error.hpp"
6 #endif
7 
8 #include <stdint.h>
9 #include <string.h>
10 #include <type_traits>
11 
12 #define C4_PREFER_BSWAP
13 
14 #if defined(C4_PREFER_BSWAP) && C4_LITTLE_ENDIAN && defined(_MSC_VER)
15 #include <intrin.h>
16 #endif
17 
18 C4_SUPPRESS_WARNING_PUSH
19 C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
20 C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
21 C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
22 C4_SUPPRESS_WARNING_GCC_CLANG("-Wchar-subscripts")
23 
24 
25 // NOLINTBEGIN(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
26 
27 namespace c4 {
28 
29 namespace {
30 
31 const char base64_sextet_to_char_[64] = {
32  /* 0/ 65*/ 'A', /* 1/ 66*/ 'B', /* 2/ 67*/ 'C', /* 3/ 68*/ 'D',
33  /* 4/ 69*/ 'E', /* 5/ 70*/ 'F', /* 6/ 71*/ 'G', /* 7/ 72*/ 'H',
34  /* 8/ 73*/ 'I', /* 9/ 74*/ 'J', /*10/ 75*/ 'K', /*11/ 74*/ 'L',
35  /*12/ 77*/ 'M', /*13/ 78*/ 'N', /*14/ 79*/ 'O', /*15/ 78*/ 'P',
36  /*16/ 81*/ 'Q', /*17/ 82*/ 'R', /*18/ 83*/ 'S', /*19/ 82*/ 'T',
37  /*20/ 85*/ 'U', /*21/ 86*/ 'V', /*22/ 87*/ 'W', /*23/ 88*/ 'X',
38  /*24/ 89*/ 'Y', /*25/ 90*/ 'Z', /*26/ 97*/ 'a', /*27/ 98*/ 'b',
39  /*28/ 99*/ 'c', /*29/100*/ 'd', /*30/101*/ 'e', /*31/102*/ 'f',
40  /*32/103*/ 'g', /*33/104*/ 'h', /*34/105*/ 'i', /*35/106*/ 'j',
41  /*36/107*/ 'k', /*37/108*/ 'l', /*38/109*/ 'm', /*39/110*/ 'n',
42  /*40/111*/ 'o', /*41/112*/ 'p', /*42/113*/ 'q', /*43/114*/ 'r',
43  /*44/115*/ 's', /*45/116*/ 't', /*46/117*/ 'u', /*47/118*/ 'v',
44  /*48/119*/ 'w', /*49/120*/ 'x', /*50/121*/ 'y', /*51/122*/ 'z',
45  /*52/ 48*/ '0', /*53/ 49*/ '1', /*54/ 50*/ '2', /*55/ 51*/ '3',
46  /*56/ 52*/ '4', /*57/ 53*/ '5', /*58/ 54*/ '6', /*59/ 55*/ '7',
47  /*60/ 56*/ '8', /*61/ 57*/ '9', /*62/ 43*/ '+', /*63/ 47*/ '/',
48 };
49 
50 using dectype = uint8_t;
51 
52 #define s_ dectype(-1) // undefined below
53 
54 // https://www.cs.cmu.edu/~pattis/15-1XX/common/handouts/ascii.html
55 const dectype base64_char_to_sextet_[128] = {
56  /* 0 NUL*/ s_, /* 1 SOH*/ s_, /* 2 STX*/ s_, /* 3 ETX*/ s_,
57  /* 4 EOT*/ s_, /* 5 ENQ*/ s_, /* 6 ACK*/ s_, /* 7 BEL*/ s_,
58  /* 8 BS */ s_, /* 9 TAB*/ s_, /* 10 LF */ s_, /* 11 VT */ s_,
59  /* 12 FF */ s_, /* 13 CR */ s_, /* 14 SO */ s_, /* 15 SI */ s_,
60  /* 16 DLE*/ s_, /* 17 DC1*/ s_, /* 18 DC2*/ s_, /* 19 DC3*/ s_,
61  /* 20 DC4*/ s_, /* 21 NAK*/ s_, /* 22 SYN*/ s_, /* 23 ETB*/ s_,
62  /* 24 CAN*/ s_, /* 25 EM */ s_, /* 26 SUB*/ s_, /* 27 ESC*/ s_,
63  /* 28 FS */ s_, /* 29 GS */ s_, /* 30 RS */ s_, /* 31 US */ s_,
64  /* 32 SPC*/ s_, /* 33 ! */ s_, /* 34 " */ s_, /* 35 # */ s_,
65  /* 36 $ */ s_, /* 37 % */ s_, /* 38 & */ s_, /* 39 ' */ s_,
66  /* 40 ( */ s_, /* 41 ) */ s_, /* 42 * */ s_, /* 43 + */ 62,
67  /* 44 , */ s_, /* 45 - */ s_, /* 46 . */ s_, /* 47 / */ 63,
68  /* 48 0 */ 52, /* 49 1 */ 53, /* 50 2 */ 54, /* 51 3 */ 55,
69  /* 52 4 */ 56, /* 53 5 */ 57, /* 54 6 */ 58, /* 55 7 */ 59,
70  /* 56 8 */ 60, /* 57 9 */ 61, /* 58 : */ s_, /* 59 ; */ s_,
71  /* 60 < */ s_, /* 61 = */ s_, /* 62 > */ s_, /* 63 ? */ s_,
72  /* 64 @ */ s_, /* 65 A */ 0, /* 66 B */ 1, /* 67 C */ 2,
73  /* 68 D */ 3, /* 69 E */ 4, /* 70 F */ 5, /* 71 G */ 6,
74  /* 72 H */ 7, /* 73 I */ 8, /* 74 J */ 9, /* 75 K */ 10,
75  /* 76 L */ 11, /* 77 M */ 12, /* 78 N */ 13, /* 79 O */ 14,
76  /* 80 P */ 15, /* 81 Q */ 16, /* 82 R */ 17, /* 83 S */ 18,
77  /* 84 T */ 19, /* 85 U */ 20, /* 86 V */ 21, /* 87 W */ 22,
78  /* 88 X */ 23, /* 89 Y */ 24, /* 90 Z */ 25, /* 91 [ */ s_,
79  /* 92 \ */ s_, /* 93 ] */ s_, /* 94 ^ */ s_, /* 95 _ */ s_,
80  /* 96 ` */ s_, /* 97 a */ 26, /* 98 b */ 27, /* 99 c */ 28,
81  /*100 d */ 29, /*101 e */ 30, /*102 f */ 31, /*103 g */ 32,
82  /*104 h */ 33, /*105 i */ 34, /*106 j */ 35, /*107 k */ 36,
83  /*108 l */ 37, /*109 m */ 38, /*110 n */ 39, /*111 o */ 40,
84  /*112 p */ 41, /*113 q */ 42, /*114 r */ 43, /*115 s */ 44,
85  /*116 t */ 45, /*117 u */ 46, /*118 v */ 47, /*119 w */ 48,
86  /*120 x */ 49, /*121 y */ 50, /*122 z */ 51, /*123 { */ s_,
87  /*124 | */ s_, /*125 } */ s_, /*126 ~ */ s_, /*127 DEL*/ s_,
88 };
89 } // namespace
90 
91 #ifndef NDEBUG
92 namespace detail {
93 C4CORE_EXPORT void base64_test_tables() // NOLINT(*use-internal-linkage*)
94 {
95  for(size_t i = 0; i < C4_COUNTOF(base64_sextet_to_char_); ++i)
96  {
97  char s2c = base64_sextet_to_char_[i];
98  dectype c2s = base64_char_to_sextet_[(unsigned)s2c];
99  C4_CHECK((size_t)c2s == i);
100  }
101  for(size_t i = 0; i < C4_COUNTOF(base64_char_to_sextet_); ++i)
102  {
103  dectype c2s = base64_char_to_sextet_[i];
104  if(c2s == s_)
105  continue;
106  char s2c = base64_sextet_to_char_[(unsigned)c2s];
107  C4_CHECK((size_t)s2c == i);
108  }
109 }
110 } // namespace detail
111 #endif
112 
113 
114 //-----------------------------------------------------------------------------
115 
116 namespace {
117 #if C4_CPP >= 17
118 C4_HOT C4_ALWAYS_INLINE bool is_valid_encoded_char_(char c) noexcept
119 {
120  if constexpr (std::is_unsigned_v<char>)
121  return ((c < 128) && (base64_char_to_sextet_[c] != s_));
122  else
123  return ((c >= 0) && (base64_char_to_sextet_[c] != s_));
124 }
125 #else // pre c++-17 implementation requires SFINAE
126 template<class Char>
127 C4_HOT C4_ALWAYS_INLINE auto is_valid_encoded_char_(Char c) noexcept
128  -> typename std::enable_if<std::is_unsigned<Char>::value, bool>::type
129 {
130  return ((c < 128) && (base64_char_to_sextet_[c] != s_));
131 }
132 template<class Char>
133 C4_HOT C4_ALWAYS_INLINE auto is_valid_encoded_char_(Char c) noexcept
134  -> typename std::enable_if< ! std::is_unsigned<Char>::value, bool>::type
135 {
136  return ((c >= 0) && (base64_char_to_sextet_[c] != s_));
137 }
138 #endif
139 
140 #undef s_
141 
142 
143 C4_HOT C4_ALWAYS_INLINE bool is_valid_encoded_group4_(const char *C4_RESTRICT c) noexcept
144 {
145  return is_valid_encoded_char_(c[0])
146  && is_valid_encoded_char_(c[1])
147  && is_valid_encoded_char_(c[2])
148  && is_valid_encoded_char_(c[3]);
149 }
150 C4_HOT C4_ALWAYS_INLINE bool is_valid_encoded_group8_(const char *C4_RESTRICT c) noexcept
151 {
152  return is_valid_encoded_char_(c[0])
153  && is_valid_encoded_char_(c[1])
154  && is_valid_encoded_char_(c[2])
155  && is_valid_encoded_char_(c[3])
156  && is_valid_encoded_char_(c[4])
157  && is_valid_encoded_char_(c[5])
158  && is_valid_encoded_char_(c[6])
159  && is_valid_encoded_char_(c[7]);
160 }
161 #if (C4_WORDSIZE >= 8)
162 C4_HOT C4_ALWAYS_INLINE bool is_valid_encoded_group16_(const char *C4_RESTRICT c, size_t num) noexcept
163 {
164  C4_ASSERT(num >= 16);
165  C4_ASSERT(!(num & 15)); // must be multiple of 16
166  size_t rem = num;
167  for( ; rem >= 16; rem -= 16, c += 16)
168  if C4_UNLIKELY(!is_valid_encoded_group8_(c)
169  || !is_valid_encoded_group8_(c + 8))
170  return false;
171  return true;
172 }
173 #endif
174 
175 
176 #ifdef C4_PREFER_BSWAP
177 # if C4_BIG_ENDIAN || (C4_MIXED_ENDIAN \
178  && defined(__BYTE_ORDER__) \
179  && (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__))
180 # define BSWAP_TO_BIG_ENDIAN64_(x)
181 # define BSWAP_TO_BIG_ENDIAN32_(x)
182 # elif C4_LITTLE_ENDIAN || (C4_MIXED_ENDIAN \
183  && defined(__BYTE_ORDER__) \
184  && (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__))
185 # ifdef _MSC_VER
186 # define BSWAP_TO_BIG_ENDIAN64_(x) (x) = _byteswap_uint64(x)
187 # define BSWAP_TO_BIG_ENDIAN32_(x) (x) = _byteswap_ulong(x)
188 # else
189 # define BSWAP_TO_BIG_ENDIAN64_(x) (x) = __builtin_bswap64(x)
190 # define BSWAP_TO_BIG_ENDIAN32_(x) (x) = __builtin_bswap32(x)
191 # endif
192 # else
193 # error not implemented
194 # endif
195 #endif
196 
197 
198 enum : uint32_t { mask32 = uint32_t((1 << 6u) - 1u) }; // NOLINT
199 #if (C4_WORDSIZE >= 8)
200 enum : uint64_t { mask64 = uint64_t((1 << 6u) - 1u) }; // NOLINT
201 C4_HOT C4_ALWAYS_INLINE void base64_encode_block64_(const uint8_t *C4_RESTRICT const data, char *C4_RESTRICT const encoded) noexcept
202 {
203  #if defined(C4_PREFER_BSWAP)
204  uint64_t val; // MSB -> LSB
205  memcpy(&val, data, sizeof(val)); // |.|.|5|4|3|2|1|0|
206  BSWAP_TO_BIG_ENDIAN64_(val); // |0|1|2|3|4|5|.|.|
207  encoded[0] = base64_sextet_to_char_[(val >> 58) & mask64];
208  encoded[1] = base64_sextet_to_char_[(val >> 52) & mask64];
209  encoded[2] = base64_sextet_to_char_[(val >> 46) & mask64];
210  encoded[3] = base64_sextet_to_char_[(val >> 40) & mask64];
211  encoded[4] = base64_sextet_to_char_[(val >> 34) & mask64];
212  encoded[5] = base64_sextet_to_char_[(val >> 28) & mask64];
213  encoded[6] = base64_sextet_to_char_[(val >> 22) & mask64];
214  encoded[7] = base64_sextet_to_char_[(val >> 16) & mask64];
215  #else
216  const uint64_t val = ((uint64_t(data[0]) << 40) | // |.|.|0|1|2|3|4|5|
217  (uint64_t(data[1]) << 32) |
218  (uint64_t(data[2]) << 24) |
219  (uint64_t(data[3]) << 16) |
220  (uint64_t(data[4]) << 8) |
221  (uint64_t(data[5])));
222  encoded[0] = base64_sextet_to_char_[(val >> 42) & mask64];
223  encoded[1] = base64_sextet_to_char_[(val >> 36) & mask64];
224  encoded[2] = base64_sextet_to_char_[(val >> 30) & mask64];
225  encoded[3] = base64_sextet_to_char_[(val >> 24) & mask64];
226  encoded[4] = base64_sextet_to_char_[(val >> 18) & mask64];
227  encoded[5] = base64_sextet_to_char_[(val >> 12) & mask64];
228  encoded[6] = base64_sextet_to_char_[(val >> 6) & mask64];
229  encoded[7] = base64_sextet_to_char_[(val ) & mask64];
230  #endif
231 }
232 #endif
233 
234 C4_HOT void base64_encode_block32_(const uint8_t *C4_RESTRICT const data, char *C4_RESTRICT const encoded) noexcept
235 {
236  #if defined(C4_PREFER_BSWAP)
237  uint32_t val = 0;
238  memcpy(&val, data, sizeof(val)); // MSB: |.|2|1|0| :LSB
239  BSWAP_TO_BIG_ENDIAN32_(val); // MSB: |0|1|2|.| :LSB
240  encoded[0] = base64_sextet_to_char_[(val >> 26) & mask32];
241  encoded[1] = base64_sextet_to_char_[(val >> 20) & mask32];
242  encoded[2] = base64_sextet_to_char_[(val >> 14) & mask32];
243  encoded[3] = base64_sextet_to_char_[(val >> 8) & mask32];
244  #else
245  // MSB: |.|0|1|2| :LSB
246  const uint32_t val = ((uint32_t(data[0]) << 16) | (uint32_t(data[1]) << 8) | (uint32_t(data[2])));
247  encoded[0] = base64_sextet_to_char_[(val >> 18) & mask32];
248  encoded[1] = base64_sextet_to_char_[(val >> 12) & mask32];
249  encoded[2] = base64_sextet_to_char_[(val >> 6) & mask32];
250  encoded[3] = base64_sextet_to_char_[(val ) & mask32];
251  #endif
252 }
253 void base64_encode_block32_term2_(const uint8_t *C4_RESTRICT data, char *C4_RESTRICT encoded) noexcept
254 {
255  // MSB: |.|.|0|1| :LSB
256  const uint32_t val = ((uint32_t(data[0]) << 16) | (uint32_t(data[1]) << 8));
257  encoded[0] = base64_sextet_to_char_[(val >> 18) & mask32];
258  encoded[1] = base64_sextet_to_char_[(val >> 12) & mask32];
259  encoded[2] = base64_sextet_to_char_[(val >> 6) & mask32];
260  encoded[3] = '=';
261 }
262 void base64_encode_block32_term1_(const uint8_t *C4_RESTRICT data, char *C4_RESTRICT encoded) noexcept
263 {
264  // MSB: |.|.|.|0| :LSB
265  const uint32_t val = ((uint32_t(data[0]) << 16));
266  encoded[0] = base64_sextet_to_char_[(val >> 18) & mask32];
267  encoded[1] = base64_sextet_to_char_[(val >> 12) & mask32];
268  encoded[2] = '=';
269  encoded[3] = '=';
270 }
271 
272 
273 //-----------------------------------------------------------------------------
274 
275 enum : uint32_t { dmask32 = 0xff }; // NOLINT
276 #if (C4_WORDSIZE >= 8)
277 enum : uint64_t { dmask64 = 0xff }; // NOLINT
278 void base64_decode_block64_(const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data) noexcept
279 {
280  uint64_t val =
281  (((uint64_t)base64_char_to_sextet_[encoded[0]]) << 42)
282  | (((uint64_t)base64_char_to_sextet_[encoded[1]]) << 36)
283  | (((uint64_t)base64_char_to_sextet_[encoded[2]]) << 30)
284  | (((uint64_t)base64_char_to_sextet_[encoded[3]]) << 24)
285  | (((uint64_t)base64_char_to_sextet_[encoded[4]]) << 18)
286  | (((uint64_t)base64_char_to_sextet_[encoded[5]]) << 12)
287  | (((uint64_t)base64_char_to_sextet_[encoded[6]]) << 6)
288  | (((uint64_t)base64_char_to_sextet_[encoded[7]]) );
289  data[0] = (dectype)((val >> 40) & dmask64);
290  data[1] = (dectype)((val >> 32) & dmask64);
291  data[2] = (dectype)((val >> 24) & dmask64);
292  data[3] = (dectype)((val >> 16) & dmask64);
293  data[4] = (dectype)((val >> 8) & dmask64);
294  data[5] = (dectype)((val ) & dmask64);
295 }
296 #endif
297 C4_HOT void base64_decode_block32_(const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data) noexcept
298 {
299  const uint32_t val =
300  (((uint32_t)base64_char_to_sextet_[encoded[0]]) << 18)
301  | (((uint32_t)base64_char_to_sextet_[encoded[1]]) << 12)
302  | (((uint32_t)base64_char_to_sextet_[encoded[2]]) << 6)
303  | (((uint32_t)base64_char_to_sextet_[encoded[3]]) );
304  data[0] = (dectype)((val >> 16) & dmask32);
305  data[1] = (dectype)((val >> 8) & dmask32);
306  data[2] = (dectype)((val ) & dmask32);
307 }
308 void base64_decode_block32_term1_(const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data) noexcept
309 {
310  const uint32_t val =
311  (((uint32_t)base64_char_to_sextet_[encoded[0]]) << 18)
312  | (((uint32_t)base64_char_to_sextet_[encoded[1]]) << 12);
313  data[0] = (dectype)((val >> 16) & dmask32);
314 }
315 void base64_decode_block32_term2_(const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data) noexcept
316 {
317  const uint32_t val =
318  (((uint32_t)base64_char_to_sextet_[encoded[0]]) << 18)
319  | (((uint32_t)base64_char_to_sextet_[encoded[1]]) << 12)
320  | (((uint32_t)base64_char_to_sextet_[encoded[2]]) << 6);
321  data[0] = (dectype)((val >> 16) & dmask32);
322  data[1] = (dectype)((val >> 8) & dmask32);
323 }
324 
325 } // namespace
326 
327 
328 //-----------------------------------------------------------------------------
329 //-----------------------------------------------------------------------------
330 //-----------------------------------------------------------------------------
331 
332 bool base64_valid(const char *encoded_, size_t encoded_sz)
333 {
334  if(!encoded_sz)
335  return true;
336  if((encoded_sz & size_t(3u))) // is it not a multiple of 4?
337  return false;
338  const char *C4_RESTRICT encoded = encoded_;
339  size_t i = 0;
340  #if C4_WORDSIZE >= 8
341  for( ; i + 8 < encoded_sz; i += 8)
342  if(!is_valid_encoded_group8_(encoded + i))
343  return false;
344  #endif
345  for( ; i + 4 < encoded_sz; i += 4)
346  if(!is_valid_encoded_group4_(encoded + i))
347  return false;
348  if(!is_valid_encoded_char_(encoded[i])
349  || !is_valid_encoded_char_(encoded[i + 1]))
350  return false;
351  if(!is_valid_encoded_char_(encoded[i + 2]))
352  return (encoded[i + 2] == '=' && encoded[i + 3] == '=');
353  if(!is_valid_encoded_char_(encoded[i + 3]))
354  return (encoded[i + 3] == '=');
355  return true;
356 }
357 
358 
359 //-----------------------------------------------------------------------------
360 
361 size_t base64_encode(char *encoded_, size_t encoded_sz, const void *data_, size_t data_sz)
362 {
363  C4_ASSERT(encoded_ != nullptr || encoded_sz == 0);
364  C4_ASSERT(data_ != nullptr || data_sz == 0);
365  // ....................... how many groups of 3 bytes to read
366  // .... each group results in 4 bytes written
367  size_t required_sz = ((data_sz + 3 - 1) / 3) * 4;
368  if(encoded_sz < required_sz)
369  return required_sz;
370  size_t rem = data_sz;
371  char *C4_RESTRICT encoded = encoded_;
372  const uint8_t *C4_RESTRICT data = (const uint8_t *) data_; // cast to unsigned to avoid wrapping high-bits
373 #if (C4_WORDSIZE >= 8)
374  for( ; rem >= 15; rem -= 12) // leave 3 at the end (15=12+3)
375  {
376  base64_encode_block64_(data, encoded); data += 6; encoded += 8;
377  base64_encode_block64_(data, encoded); data += 6; encoded += 8;
378  }
379  for( ; rem >= 9; rem -= 6) // leave 3 at the end (9=6+3)
380  {
381  base64_encode_block64_(data, encoded); data += 6; encoded += 8;
382  }
383 #else
384  for( ; rem >= 15; rem -= 12) // leave 3 at the end (15=12+3)
385  {
386  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
387  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
388  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
389  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
390  }
391  for( ; rem >= 9; rem -= 6) // leave 3 at the end (9=6+3)
392  {
393  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
394  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
395  }
396 #endif
397  for( ; rem >= 3; rem -= 3)
398  {
399  base64_encode_block32_(data, encoded); data += 3; encoded += 4;
400  }
401  C4_ASSERT(rem < 3);
402  if(rem == 2)
403  base64_encode_block32_term2_(data, encoded);
404  else if(rem == 1)
405  base64_encode_block32_term1_(data, encoded);
406  return required_sz;
407 }
408 
409 
410 //-----------------------------------------------------------------------------
411 
412 bool base64_decode(char const* encoded_, size_t encoded_sz,
413  void * data_, size_t data_sz,
414  size_t *data_sz_required)
415 {
416  C4_ASSERT(encoded_ != nullptr || encoded_sz == 0);
417  C4_ASSERT(data_ != nullptr || data_sz == 0);
418  C4_ASSERT(data_sz_required != nullptr);
419  if(!encoded_sz)
420  {
421  *data_sz_required = 0;
422  return true;
423  }
424  else if(encoded_sz & 3u) // is encoded_sz not a multiple of 4?
425  {
426  return false;
427  }
428  // compute the required size for the decoded buffer:
429  // ................ how many 4-byte groups of encoded data to decode
430  // .... each group results in 3 decoded bytes
431  *data_sz_required = (encoded_sz / 4) * 3;
432  const char *C4_RESTRICT encoded = encoded_;
433  // account for padded bytes at the end
434  C4_ASSERT(encoded_sz >= 4);
435  if(encoded[encoded_sz - 1] == '=')
436  {
437  C4_ASSERT(*data_sz_required >= 3);
438  if(encoded[encoded_sz - 2] == '=')
439  *data_sz_required -= 2;
440  else
441  *data_sz_required -= 1;
442  }
443  if(data_sz < *data_sz_required)
444  return false;
445  // we have enough room
446  size_t rem = *data_sz_required; // numbytes remaining to write
447  dectype *C4_RESTRICT data = (dectype *)data_;
448  C4_STATIC_ASSERT(sizeof(dectype) == 1);
449 #if (C4_WORDSIZE >= 8)
450  for( ; rem >= 15; rem -= 12)
451  {
452  if C4_UNLIKELY(!is_valid_encoded_group16_(encoded, 16))
453  return false;
454  base64_decode_block64_(encoded, data); encoded += 8; data += 6;
455  base64_decode_block64_(encoded, data); encoded += 8; data += 6;
456  }
457  for( ; rem >= 9; rem -= 6)
458  {
459  if C4_UNLIKELY(!is_valid_encoded_group8_(encoded))
460  return false;
461  base64_decode_block64_(encoded, data); encoded += 8; data += 6;
462  }
463 #else
464  for( ; rem >= 9; rem -= 6)
465  {
466  if C4_UNLIKELY(!is_valid_encoded_group8_(encoded))
467  return false;
468  base64_decode_block32_(encoded, data); encoded += 4; data += 3;
469  base64_decode_block32_(encoded, data); encoded += 4; data += 3;
470  }
471 #endif
472  for( ; rem >= 3; rem -= 3)
473  {
474  if C4_UNLIKELY(!is_valid_encoded_group4_(encoded))
475  return false;
476  base64_decode_block32_(encoded, data); encoded += 4; data += 3;
477  }
478  C4_ASSERT(rem < 3);
479  // the last quartet requires dealing with padded chars
480  if(rem == 1) // 1 remaining byte, 2 padding chars
481  {
482  if(!is_valid_encoded_char_(encoded[0])
483  || !is_valid_encoded_char_(encoded[1])
484  || encoded[2] != '='
485  || encoded[3] != '=')
486  return false;
487  base64_decode_block32_term1_(encoded, data);
488  }
489  else if(rem == 2) // 2 remaining bytes, 1 padding char
490  {
491  if(!is_valid_encoded_char_(encoded[0])
492  || !is_valid_encoded_char_(encoded[1])
493  || !is_valid_encoded_char_(encoded[2])
494  || encoded[3] != '=')
495  return false;
496  base64_decode_block32_term2_(encoded, data);
497  }
498  return true;
499 }
500 
501 } // namespace c4
502 
503 // NOLINTEND(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
504 
505 C4_SUPPRESS_WARNING_POP
bool base64_valid(const char *encoded, size_t encoded_sz)
check that the given buffer is a valid base64 encoding
Definition: base64.cpp:332
encoding/decoding for base64.
void base64_test_tables()
Definition: base64.cpp:93
size_t base64_encode(char *encoded_, size_t encoded_sz, const void *data_, size_t data_sz)
Definition: base64.cpp:361
#define s_
Definition: base64.cpp:52
bool base64_decode(char const *encoded, size_t encoded_sz, void *data, size_t data_sz, size_t *data_sz_required)
decode the base64 encoding in the given buffer.
Definition: base64.cpp:412