12#define C4_PREFER_BSWAP
14#if defined(C4_PREFER_BSWAP) && C4_LITTLE_ENDIAN && defined(_MSC_VER)
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")
31const char base64_sextet_to_char_[64] = {
50using dectype = uint8_t;
55const dectype base64_char_to_sextet_[128] = {
95 for(
size_t i = 0; i < C4_COUNTOF(base64_sextet_to_char_); ++i)
97 char s2c = base64_sextet_to_char_[i];
98 dectype c2s = base64_char_to_sextet_[(unsigned)s2c];
99 C4_CHECK((
size_t)c2s == i);
101 for(
size_t i = 0; i < C4_COUNTOF(base64_char_to_sextet_); ++i)
103 dectype c2s = base64_char_to_sextet_[i];
106 char s2c = base64_sextet_to_char_[(unsigned)c2s];
107 C4_CHECK((
size_t)s2c == i);
118C4_HOT C4_ALWAYS_INLINE
bool is_valid_encoded_char_(
char c)
noexcept
120 if constexpr (std::is_unsigned_v<char>)
121 return ((c < 128) && (base64_char_to_sextet_[c] !=
s_));
123 return ((c >= 0) && (base64_char_to_sextet_[c] !=
s_));
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
130 return ((c < 128) && (base64_char_to_sextet_[c] !=
s_));
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
136 return ((c >= 0) && (base64_char_to_sextet_[c] !=
s_));
143C4_HOT C4_ALWAYS_INLINE
bool is_valid_encoded_group4_(
const char *C4_RESTRICT c)
noexcept
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]);
150C4_HOT C4_ALWAYS_INLINE
bool is_valid_encoded_group8_(
const char *C4_RESTRICT c)
noexcept
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]);
161#if (C4_WORDSIZE >= 8)
162C4_HOT C4_ALWAYS_INLINE
bool is_valid_encoded_group16_(
const char *C4_RESTRICT c,
size_t num)
noexcept
164 C4_ASSERT(num >= 16);
165 C4_ASSERT(!(num & 15));
167 for( ; rem >= 16; rem -= 16, c += 16)
168 if C4_UNLIKELY(!is_valid_encoded_group8_(c)
169 || !is_valid_encoded_group8_(c + 8))
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__))
186# define BSWAP_TO_BIG_ENDIAN64_(x) (x) = _byteswap_uint64(x)
187# define BSWAP_TO_BIG_ENDIAN32_(x) (x) = _byteswap_ulong(x)
189# define BSWAP_TO_BIG_ENDIAN64_(x) (x) = __builtin_bswap64(x)
190# define BSWAP_TO_BIG_ENDIAN32_(x) (x) = __builtin_bswap32(x)
193# error not implemented
198enum : uint32_t { mask32 = uint32_t((1 << 6u) - 1u) };
199#if (C4_WORDSIZE >= 8)
200enum : uint64_t { mask64 = uint64_t((1 << 6u) - 1u) };
201C4_HOT C4_ALWAYS_INLINE
void base64_encode_block64_(
const uint8_t *C4_RESTRICT
const data,
char *C4_RESTRICT
const encoded)
noexcept
203 #if defined(C4_PREFER_BSWAP)
205 memcpy(&val, data,
sizeof(val));
206 BSWAP_TO_BIG_ENDIAN64_(val);
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];
216 const uint64_t val = ((uint64_t(data[0]) << 40) |
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];
234C4_HOT
void base64_encode_block32_(
const uint8_t *C4_RESTRICT
const data,
char *C4_RESTRICT
const encoded)
noexcept
236 #if defined(C4_PREFER_BSWAP)
238 memcpy(&val, data,
sizeof(val));
239 BSWAP_TO_BIG_ENDIAN32_(val);
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];
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];
253void base64_encode_block32_term2_(
const uint8_t *C4_RESTRICT data,
char *C4_RESTRICT encoded)
noexcept
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];
262void base64_encode_block32_term1_(
const uint8_t *C4_RESTRICT data,
char *C4_RESTRICT encoded)
noexcept
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];
275enum : uint32_t { dmask32 = 0xff };
276#if (C4_WORDSIZE >= 8)
277enum : uint64_t { dmask64 = 0xff };
278void base64_decode_block64_(
const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data)
noexcept
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);
297C4_HOT
void base64_decode_block32_(
const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data)
noexcept
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);
308void base64_decode_block32_term1_(
const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data)
noexcept
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);
315void base64_decode_block32_term2_(
const char *C4_RESTRICT encoded, dectype *C4_RESTRICT data)
noexcept
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);
336 if((encoded_sz &
size_t(3u)))
338 const char *C4_RESTRICT encoded = encoded_;
341 for( ; i + 8 < encoded_sz; i += 8)
342 if(!is_valid_encoded_group8_(encoded + i))
345 for( ; i + 4 < encoded_sz; i += 4)
346 if(!is_valid_encoded_group4_(encoded + i))
348 if(!is_valid_encoded_char_(encoded[i])
349 || !is_valid_encoded_char_(encoded[i + 1]))
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] ==
'=');
361size_t base64_encode(
char *encoded_,
size_t encoded_sz,
const void *data_,
size_t data_sz)
363 C4_ASSERT(encoded_ !=
nullptr || encoded_sz == 0);
364 C4_ASSERT(data_ !=
nullptr || data_sz == 0);
367 size_t required_sz = ((data_sz + 3 - 1) / 3) * 4;
368 if(encoded_sz < 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_;
373#if (C4_WORDSIZE >= 8)
374 for( ; rem >= 15; rem -= 12)
376 base64_encode_block64_(data, encoded); data += 6; encoded += 8;
377 base64_encode_block64_(data, encoded); data += 6; encoded += 8;
379 for( ; rem >= 9; rem -= 6)
381 base64_encode_block64_(data, encoded); data += 6; encoded += 8;
384 for( ; rem >= 15; rem -= 12)
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;
391 for( ; rem >= 9; rem -= 6)
393 base64_encode_block32_(data, encoded); data += 3; encoded += 4;
394 base64_encode_block32_(data, encoded); data += 3; encoded += 4;
397 for( ; rem >= 3; rem -= 3)
399 base64_encode_block32_(data, encoded); data += 3; encoded += 4;
403 base64_encode_block32_term2_(data, encoded);
405 base64_encode_block32_term1_(data, encoded);
413 void * data_,
size_t data_sz,
414 size_t *data_sz_required)
416 C4_ASSERT(encoded_ !=
nullptr || encoded_sz == 0);
417 C4_ASSERT(data_ !=
nullptr || data_sz == 0);
418 C4_ASSERT(data_sz_required !=
nullptr);
421 *data_sz_required = 0;
424 else if(encoded_sz & 3u)
431 *data_sz_required = (encoded_sz / 4) * 3;
432 const char *C4_RESTRICT encoded = encoded_;
434 C4_ASSERT(encoded_sz >= 4);
435 if(encoded[encoded_sz - 1] ==
'=')
437 C4_ASSERT(*data_sz_required >= 3);
438 if(encoded[encoded_sz - 2] ==
'=')
439 *data_sz_required -= 2;
441 *data_sz_required -= 1;
443 if(data_sz < *data_sz_required)
446 size_t rem = *data_sz_required;
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)
452 if C4_UNLIKELY(!is_valid_encoded_group16_(encoded, 16))
454 base64_decode_block64_(encoded, data); encoded += 8; data += 6;
455 base64_decode_block64_(encoded, data); encoded += 8; data += 6;
457 for( ; rem >= 9; rem -= 6)
459 if C4_UNLIKELY(!is_valid_encoded_group8_(encoded))
461 base64_decode_block64_(encoded, data); encoded += 8; data += 6;
464 for( ; rem >= 9; rem -= 6)
466 if C4_UNLIKELY(!is_valid_encoded_group8_(encoded))
468 base64_decode_block32_(encoded, data); encoded += 4; data += 3;
469 base64_decode_block32_(encoded, data); encoded += 4; data += 3;
472 for( ; rem >= 3; rem -= 3)
474 if C4_UNLIKELY(!is_valid_encoded_group4_(encoded))
476 base64_decode_block32_(encoded, data); encoded += 4; data += 3;
482 if(!is_valid_encoded_char_(encoded[0])
483 || !is_valid_encoded_char_(encoded[1])
485 || encoded[3] !=
'=')
487 base64_decode_block32_term1_(encoded, data);
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] !=
'=')
496 base64_decode_block32_term2_(encoded, data);
505C4_SUPPRESS_WARNING_POP
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.
size_t base64_encode(char *encoded, size_t encoded_sz, void const *data, size_t data_sz)
base64-encode binary data.
bool base64_valid(const char *encoded, size_t encoded_sz)
check that the given buffer is a valid base64 encoding
void base64_test_tables()