rapidyaml 0.15.2
parse and emit YAML, and do it fast
Loading...
Searching...
No Matches
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
18C4_SUPPRESS_WARNING_PUSH
19C4_SUPPRESS_WARNING_GCC("-Wtype-limits")
20C4_SUPPRESS_WARNING_GCC("-Wuseless-cast")
21C4_SUPPRESS_WARNING_GCC_CLANG("-Wold-style-cast")
22C4_SUPPRESS_WARNING_GCC_CLANG("-Wchar-subscripts")
23
24
25// NOLINTBEGIN(bugprone-signed-char-misuse,cert-str34-c,hicpp-signed-bitwise)
26
27namespace c4 {
28
29namespace {
30
31const 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
50using 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
55const 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
92namespace detail {
93C4CORE_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
116namespace {
117#if C4_CPP >= 17
118C4_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
126template<class Char>
127C4_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}
132template<class Char>
133C4_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
143C4_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}
150C4_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)
162C4_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
198enum : uint32_t { mask32 = uint32_t((1 << 6u) - 1u) }; // NOLINT
199#if (C4_WORDSIZE >= 8)
200enum : uint64_t { mask64 = uint64_t((1 << 6u) - 1u) }; // NOLINT
201C4_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
234C4_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}
253void 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}
262void 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
275enum : uint32_t { dmask32 = 0xff }; // NOLINT
276#if (C4_WORDSIZE >= 8)
277enum : uint64_t { dmask64 = 0xff }; // NOLINT
278void 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
297C4_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}
308void 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}
315void 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
332bool 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
361size_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
412bool 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
505C4_SUPPRESS_WARNING_POP
#define s_
Definition base64.cpp:52
encoding/decoding for base64.
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
size_t base64_encode(char *encoded, size_t encoded_sz, void const *data, size_t data_sz)
base64-encode binary data.
Definition base64.cpp:361
bool base64_valid(const char *encoded, size_t encoded_sz)
check that the given buffer is a valid base64 encoding
Definition base64.cpp:332
void base64_test_tables()
Definition base64.cpp:93