1#ifndef SIMDUTF_BASE64_H
2#define SIMDUTF_BASE64_H
15simdutf_really_inline simdutf_constexpr23
void
16copy_encode_pair(
char *dst,
const std::array<char, 2> &pair) {
17#if SIMDUTF_CPLUSPLUS23
18 if (std::is_constant_evaluated()) {
24 std::memcpy(dst, pair.data(), pair.size());
29template <
class char_type>
bool is_ascii_white_space(char_type c) {
30 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f';
33template <
class char_type> simdutf_constexpr23
bool is_eight_byte(char_type c) {
34 if constexpr (
sizeof(char_type) == 1) {
37 return uint8_t(c) == c;
40template <
class char_type>
41simdutf_constexpr23
bool is_ignorable(char_type c,
42 simdutf::base64_options options) {
43 const uint8_t *to_base64 =
44 (options & base64_default_or_url)
45 ? tables::base64::to_base64_default_or_url_value
46 : ((options & base64_url) ? tables::base64::to_base64_url_value
47 : tables::base64::to_base64_value);
48 const bool ignore_garbage =
49 (options == base64_options::base64_url_accept_garbage) ||
50 (options == base64_options::base64_default_accept_garbage) ||
51 (options == base64_options::base64_default_or_url_accept_garbage);
52 uint8_t code = to_base64[uint8_t(c)];
53 if (is_eight_byte(c) && code <= 63) {
56 if (is_eight_byte(c) && code == 64) {
59 return ignore_garbage;
61template <
class char_type>
62simdutf_constexpr23
bool is_base64(char_type c,
63 simdutf::base64_options options) {
64 const uint8_t *to_base64 =
65 (options & base64_default_or_url)
66 ? tables::base64::to_base64_default_or_url_value
67 : ((options & base64_url) ? tables::base64::to_base64_url_value
68 : tables::base64::to_base64_value);
69 uint8_t code = to_base64[uint8_t(c)];
70 if (is_eight_byte(c) && code <= 63) {
76template <
class char_type>
77simdutf_constexpr23
bool is_base64_or_padding(char_type c,
78 simdutf::base64_options options) {
79 const uint8_t *to_base64 =
80 (options & base64_default_or_url)
81 ? tables::base64::to_base64_default_or_url_value
82 : ((options & base64_url) ? tables::base64::to_base64_url_value
83 : tables::base64::to_base64_value);
87 uint8_t code = to_base64[uint8_t(c)];
88 if (is_eight_byte(c) && code <= 63) {
94template <
class char_type>
95bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) {
96 return is_ignorable(c, options) || c ==
'=';
101 size_t equallocation;
103 size_t full_input_length;
112template <
class char_type>
113simdutf_constexpr23 reduced_input find_end(
const char_type *src,
size_t srclen,
114 simdutf::base64_options options) {
115 const uint8_t *to_base64 =
116 (options & base64_default_or_url)
117 ? tables::base64::to_base64_default_or_url_value
118 : ((options & base64_url) ? tables::base64::to_base64_url_value
119 : tables::base64::to_base64_value);
120 const bool ignore_garbage =
121 (options == base64_options::base64_url_accept_garbage) ||
122 (options == base64_options::base64_default_accept_garbage) ||
123 (options == base64_options::base64_default_or_url_accept_garbage);
125 size_t equalsigns = 0;
128 size_t full_input_length = srclen;
130 while (!ignore_garbage && srclen > 0 &&
131 scalar::base64::is_eight_byte(src[srclen - 1]) &&
132 to_base64[uint8_t(src[srclen - 1])] == 64) {
135 size_t equallocation =
137 if (ignore_garbage) {
141 if (it != src + srclen) {
142 equallocation = it - src;
144 srclen = equallocation;
145 full_input_length = equallocation + 1;
147 return {equalsigns, equallocation, srclen, full_input_length};
149 if (!ignore_garbage && srclen > 0 && src[srclen - 1] ==
'=') {
151 equallocation = srclen - 1;
155 while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
156 to_base64[uint8_t(src[srclen - 1])] == 64) {
159 if (srclen > 0 && src[srclen - 1] ==
'=') {
161 equallocation = srclen - 1;
166 return {equalsigns, equallocation, srclen, full_input_length};
173template <
bool check_capacity,
class char_type>
174simdutf_constexpr23 full_result base64_tail_decode_impl(
175 char *dst,
size_t outlen,
const char_type *src,
size_t length,
176 size_t padding_characters,
178 base64_options options, last_chunk_handling_options last_chunk_options) {
179 char *dstend = dst + outlen;
183 const uint8_t *to_base64 =
184 (options & base64_default_or_url)
185 ? tables::base64::to_base64_default_or_url_value
186 : ((options & base64_url) ? tables::base64::to_base64_url_value
187 : tables::base64::to_base64_value);
189 (options & base64_default_or_url)
190 ? tables::base64::base64_default_or_url::d0
191 : ((options & base64_url) ? tables::base64::base64_url::d0
192 : tables::base64::base64_default::d0);
194 (options & base64_default_or_url)
195 ? tables::base64::base64_default_or_url::d1
196 : ((options & base64_url) ? tables::base64::base64_url::d1
197 : tables::base64::base64_default::d1);
199 (options & base64_default_or_url)
200 ? tables::base64::base64_default_or_url::d2
201 : ((options & base64_url) ? tables::base64::base64_url::d2
202 : tables::base64::base64_default::d2);
204 (options & base64_default_or_url)
205 ? tables::base64::base64_default_or_url::d3
206 : ((options & base64_url) ? tables::base64::base64_url::d3
207 : tables::base64::base64_default::d3);
208 const bool ignore_garbage =
209 (options == base64_options::base64_url_accept_garbage) ||
210 (options == base64_options::base64_default_accept_garbage) ||
211 (options == base64_options::base64_default_or_url_accept_garbage);
213 const char_type *srcend = src + length;
214 const char_type *srcinit = src;
215 const char *dstinit = dst;
221 while (srcend - src >= 12 && (!check_capacity || dstend - dst >= 9) &&
222 is_eight_byte(src[0]) && is_eight_byte(src[1]) &&
223 is_eight_byte(src[2]) && is_eight_byte(src[3]) &&
224 is_eight_byte(src[4]) && is_eight_byte(src[5]) &&
225 is_eight_byte(src[6]) && is_eight_byte(src[7]) &&
226 is_eight_byte(src[8]) && is_eight_byte(src[9]) &&
227 is_eight_byte(src[10]) && is_eight_byte(src[11])) {
228 const uint32_t x0 = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
229 d2[uint8_t(src[2])] | d3[uint8_t(src[3])];
230 const uint32_t x1 = d0[uint8_t(src[4])] | d1[uint8_t(src[5])] |
231 d2[uint8_t(src[6])] | d3[uint8_t(src[7])];
232 const uint32_t x2 = d0[uint8_t(src[8])] | d1[uint8_t(src[9])] |
233 d2[uint8_t(src[10])] | d3[uint8_t(src[11])];
234 if ((x0 | x1 | x2) >= 0x01FFFFFF) {
237#if SIMDUTF_IS_BIG_ENDIAN
238 dst[0] =
static_cast<char>(x0);
239 dst[1] =
static_cast<char>(x0 >> 8);
240 dst[2] =
static_cast<char>(x0 >> 16);
241 dst[3] =
static_cast<char>(x1);
242 dst[4] =
static_cast<char>(x1 >> 8);
243 dst[5] =
static_cast<char>(x1 >> 16);
244 dst[6] =
static_cast<char>(x2);
245 dst[7] =
static_cast<char>(x2 >> 8);
246 dst[8] =
static_cast<char>(x2 >> 16);
248 const uint64_t word =
249 uint64_t(x0) | (uint64_t(x1) << 24) | (uint64_t(x2) << 48);
250 #if SIMDUTF_CPLUSPLUS23
251 if (std::is_constant_evaluated()) {
252 dst[0] =
static_cast<char>(x0);
253 dst[1] =
static_cast<char>(x0 >> 8);
254 dst[2] =
static_cast<char>(x0 >> 16);
255 dst[3] =
static_cast<char>(x1);
256 dst[4] =
static_cast<char>(x1 >> 8);
257 dst[5] =
static_cast<char>(x1 >> 16);
258 dst[6] =
static_cast<char>(x2);
259 dst[7] =
static_cast<char>(x2 >> 8);
263 std::memcpy(dst, &word,
sizeof(word));
265 dst[8] =
static_cast<char>(x2 >> 16);
270 while (srcend - src >= 4 && is_eight_byte(src[0]) &&
271 is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
272 is_eight_byte(src[3]) &&
273 (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
274 d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
275 if (check_capacity && dstend - dst < 3) {
276 return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
277 size_t(dst - dstinit)};
279 *dst++ =
static_cast<char>(x & 0xFF);
280 *dst++ =
static_cast<char>((x >> 8) & 0xFF);
281 *dst++ =
static_cast<char>((x >> 16) & 0xFF);
284 const char_type *srccur = src;
289 if (ignore_garbage && src + 4 <= srcend) {
290 char_type c0 = src[0];
291 char_type c1 = src[1];
292 char_type c2 = src[2];
293 char_type c3 = src[3];
295 uint8_t code0 = to_base64[uint8_t(c0)];
296 uint8_t code1 = to_base64[uint8_t(c1)];
297 uint8_t code2 = to_base64[uint8_t(c2)];
298 uint8_t code3 = to_base64[uint8_t(c3)];
301 idx += (is_eight_byte(c0) && code0 <= 63);
303 idx += (is_eight_byte(c1) && code1 <= 63);
305 idx += (is_eight_byte(c2) && code2 <= 63);
307 idx += (is_eight_byte(c3) && code3 <= 63);
311 while ((idx < 4) && (src < srcend)) {
314 uint8_t code = to_base64[uint8_t(c)];
315 buffer[idx] = uint8_t(code);
316 if (is_eight_byte(c) && code <= 63) {
318 }
else if (!ignore_garbage &&
319 (code > 64 || !scalar::base64::is_eight_byte(c))) {
320 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
321 size_t(dst - dstinit)};
328 simdutf_log_assert(idx < 4,
"idx should be less than 4");
331 if (!ignore_garbage && (idx + padding_characters > 4)) {
332 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
333 size_t(dst - dstinit),
true};
340 if (!ignore_garbage &&
341 last_chunk_options == last_chunk_handling_options::loose &&
342 (idx >= 2) && padding_characters > 0 &&
343 ((idx + padding_characters) & 3) != 0) {
344 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
345 size_t(dst - dstinit),
true};
351 if (!ignore_garbage &&
352 last_chunk_options == last_chunk_handling_options::strict &&
353 (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
355 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
356 size_t(dst - dstinit),
true};
361 if ((last_chunk_options ==
362 last_chunk_handling_options::stop_before_partial &&
363 (padding_characters + idx < 4) && (idx != 0) &&
364 (idx >= 2 || padding_characters == 0)) ||
365 (last_chunk_options ==
366 last_chunk_handling_options::only_full_chunks &&
367 (idx >= 2 || padding_characters == 0))) {
371 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
374 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
375 (uint32_t(buffer[1]) << 2 * 6);
376 if (!ignore_garbage &&
377 (last_chunk_options == last_chunk_handling_options::strict) &&
379 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
380 size_t(dst - dstinit)};
382 if (check_capacity && dstend - dst < 1) {
383 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
384 size_t(dst - dstinit)};
386 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
387 }
else if (idx == 3) {
388 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
389 (uint32_t(buffer[1]) << 2 * 6) +
390 (uint32_t(buffer[2]) << 1 * 6);
391 if (!ignore_garbage &&
392 (last_chunk_options == last_chunk_handling_options::strict) &&
394 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
395 size_t(dst - dstinit)};
397 if (check_capacity && dstend - dst < 2) {
398 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
399 size_t(dst - dstinit)};
401 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
402 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
403 }
else if (!ignore_garbage && idx == 1 &&
404 (!is_partial(last_chunk_options) ||
405 (is_partial(last_chunk_options) &&
406 padding_characters > 0))) {
407 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
408 size_t(dst - dstinit)};
409 }
else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
410 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
411 size_t(dst - dstinit),
true};
413 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
416 if (check_capacity && dstend - dst < 3) {
417 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
418 size_t(dst - dstinit)};
421 (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
422 (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
423 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
424 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
425 *dst++ =
static_cast<char>(triple & 0xFF);
429template <
class char_type>
430simdutf_constexpr23 full_result base64_tail_decode(
431 char *dst,
const char_type *src,
size_t length,
432 size_t padding_characters,
434 base64_options options, last_chunk_handling_options last_chunk_options) {
435 return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
436 options, last_chunk_options);
443template <
class char_type>
444simdutf_constexpr23 full_result base64_tail_decode_safe(
445 char *dst,
size_t outlen,
const char_type *src,
size_t length,
446 size_t padding_characters,
448 base64_options options, last_chunk_handling_options last_chunk_options) {
449 return base64_tail_decode_impl<true>(dst, outlen, src, length,
450 padding_characters, options,
454inline simdutf_constexpr23 full_result
455patch_tail_result(full_result r,
size_t previous_input,
size_t previous_output,
456 size_t equallocation,
size_t full_input_length,
457 last_chunk_handling_options last_chunk_options) {
458 r.input_count += previous_input;
459 r.output_count += previous_output;
460 if (r.padding_error) {
461 r.input_count = equallocation;
464 if (r.error == error_code::SUCCESS) {
465 if (!is_partial(last_chunk_options)) {
468 r.input_count = full_input_length;
469 }
else if (r.output_count % 3 != 0) {
470 r.input_count = full_input_length;
476simdutf_really_inline simdutf_constexpr23
void
477tail_encode_base64_quantum(
char *dst,
const char *src,
478 const std::array<char, 2> *pairs) {
479 const uint32_t value = (uint32_t(uint8_t(src[0])) << 16) |
480 (uint32_t(uint8_t(src[1])) << 8) | uint8_t(src[2]);
481 copy_encode_pair(dst, pairs[value >> 12]);
482 copy_encode_pair(dst + 2, pairs[value & 0xFFF]);
485simdutf_really_inline simdutf_constexpr23
size_t tail_encode_base64_short_impl(
486 char *dst,
const char *src,
size_t srclen, base64_options options) {
487 const auto *pairs = (options & base64_url)
488 ? tables::base64::base64_url::encode_pairs.data()
489 : tables::base64::base64_default::encode_pairs.data();
490 const bool use_padding =
491 ((options & base64_url) == 0) ^
492 ((options & base64_reverse_padding) == base64_reverse_padding);
494#define SIMDUTF_BASE64_ENCODE_QUANTUM(index) \
495 tail_encode_base64_quantum(dst + (index) * 4, src + (index) * 3, pairs)
496#define SIMDUTF_BASE64_ENCODE_TAIL1(index) \
498 const uint32_t value = uint32_t(uint8_t(src[(index) * 3])) << 16; \
499 copy_encode_pair(dst + (index) * 4, pairs[value >> 12]); \
501 dst[(index) * 4 + 2] = '='; \
502 dst[(index) * 4 + 3] = '='; \
505#define SIMDUTF_BASE64_ENCODE_TAIL2(index) \
507 const uint32_t value = (uint32_t(uint8_t(src[(index) * 3])) << 16) | \
508 (uint32_t(uint8_t(src[(index) * 3 + 1])) << 8); \
509 copy_encode_pair(dst + (index) * 4, pairs[value >> 12]); \
510 dst[(index) * 4 + 2] = pairs[value & 0xFFF][0]; \
512 dst[(index) * 4 + 3] = '='; \
518 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
519 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
520 SIMDUTF_BASE64_ENCODE_QUANTUM(2);
521 SIMDUTF_BASE64_ENCODE_TAIL2(3);
522 return 15 + size_t(use_padding);
524 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
525 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
526 SIMDUTF_BASE64_ENCODE_QUANTUM(2);
527 SIMDUTF_BASE64_ENCODE_TAIL1(3);
528 return 14 + 2 * size_t(use_padding);
530 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
531 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
532 SIMDUTF_BASE64_ENCODE_QUANTUM(2);
535 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
536 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
537 SIMDUTF_BASE64_ENCODE_TAIL2(2);
538 return 11 + size_t(use_padding);
540 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
541 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
542 SIMDUTF_BASE64_ENCODE_TAIL1(2);
543 return 10 + 2 * size_t(use_padding);
545 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
546 SIMDUTF_BASE64_ENCODE_QUANTUM(1);
549 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
550 SIMDUTF_BASE64_ENCODE_TAIL2(1);
551 return 7 + size_t(use_padding);
553 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
554 SIMDUTF_BASE64_ENCODE_TAIL1(1);
555 return 6 + 2 * size_t(use_padding);
557 SIMDUTF_BASE64_ENCODE_QUANTUM(0);
560 SIMDUTF_BASE64_ENCODE_TAIL2(0);
561 return 3 + size_t(use_padding);
563 SIMDUTF_BASE64_ENCODE_TAIL1(0);
564 return 2 + 2 * size_t(use_padding);
569#undef SIMDUTF_BASE64_ENCODE_TAIL2
570#undef SIMDUTF_BASE64_ENCODE_TAIL1
571#undef SIMDUTF_BASE64_ENCODE_QUANTUM
574simdutf_never_inline simdutf_constexpr23
size_t tail_encode_base64_long_impl(
575 char *dst,
const char *src,
size_t srclen, base64_options options) {
576 const auto *pairs = (options & base64_url)
577 ? tables::base64::base64_url::encode_pairs.data()
578 : tables::base64::base64_default::encode_pairs.data();
581 for (; i + 11 < srclen; i += 12) {
582 const uint32_t v0 = (uint32_t(uint8_t(src[i])) << 16) |
583 (uint32_t(uint8_t(src[i + 1])) << 8) |
585 const uint32_t v1 = (uint32_t(uint8_t(src[i + 3])) << 16) |
586 (uint32_t(uint8_t(src[i + 4])) << 8) |
588 const uint32_t v2 = (uint32_t(uint8_t(src[i + 6])) << 16) |
589 (uint32_t(uint8_t(src[i + 7])) << 8) |
591 const uint32_t v3 = (uint32_t(uint8_t(src[i + 9])) << 16) |
592 (uint32_t(uint8_t(src[i + 10])) << 8) |
593 uint8_t(src[i + 11]);
594 copy_encode_pair(out, pairs[v0 >> 12]);
595 copy_encode_pair(out + 2, pairs[v0 & 0xFFF]);
596 copy_encode_pair(out + 4, pairs[v1 >> 12]);
597 copy_encode_pair(out + 6, pairs[v1 & 0xFFF]);
598 copy_encode_pair(out + 8, pairs[v2 >> 12]);
599 copy_encode_pair(out + 10, pairs[v2 & 0xFFF]);
600 copy_encode_pair(out + 12, pairs[v3 >> 12]);
601 copy_encode_pair(out + 14, pairs[v3 & 0xFFF]);
604 return size_t(out - dst);
609template <
bool use_lines = false>
610simdutf_constexpr23
size_t tail_encode_base64_impl(
611 char *dst,
const char *src,
size_t srclen, base64_options options,
613 if constexpr (!use_lines) {
615 return tail_encode_base64_short_impl(dst, src, srclen, options);
617 const size_t i = srclen - srclen % 12;
618 const size_t written = tail_encode_base64_long_impl(dst, src, i, options);
619 return written + tail_encode_base64_short_impl(dst + written, src + i,
620 srclen - i, options);
624 if (line_length < 4) {
627 simdutf_log_assert(line_offset <= line_length,
628 "line_offset should be less than line_length");
638 ((options & base64_url) == 0) ^
639 ((options & base64_reverse_padding) == base64_reverse_padding);
642 const char *e0 = (options & base64_url)
643 ? tables::base64::base64_url::e0
644 : tables::base64::base64_default::e0;
645 const char *e1 = (options & base64_url)
646 ? tables::base64::base64_url::e1
647 : tables::base64::base64_default::e1;
648 const char *e2 = (options & base64_url)
649 ? tables::base64::base64_url::e2
650 : tables::base64::base64_default::e2;
654 for (; i + 2 < srclen; i += 3) {
655 t1 = uint8_t(src[i]);
656 t2 = uint8_t(src[i + 1]);
657 t3 = uint8_t(src[i + 2]);
658 if (line_offset + 3 >= line_length) {
659 if (line_offset == line_length) {
662 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
663 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
666 }
else if (line_offset + 1 == line_length) {
669 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
670 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
673 }
else if (line_offset + 2 == line_length) {
675 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
677 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
680 }
else if (line_offset + 3 == line_length) {
682 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
683 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
690 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
691 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
696 switch (srclen - i) {
700 t1 = uint8_t(src[i]);
702 if (line_offset + 3 >= line_length) {
703 if (line_offset == line_length) {
706 *out++ = e1[(t1 & 0x03) << 4];
709 }
else if (line_offset + 1 == line_length) {
712 *out++ = e1[(t1 & 0x03) << 4];
715 }
else if (line_offset + 2 == line_length) {
717 *out++ = e1[(t1 & 0x03) << 4];
721 }
else if (line_offset + 3 == line_length) {
723 *out++ = e1[(t1 & 0x03) << 4];
730 *out++ = e1[(t1 & 0x03) << 4];
735 if (line_offset + 2 >= line_length) {
736 if (line_offset == line_length) {
738 *out++ = e0[uint8_t(src[i])];
739 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
740 }
else if (line_offset + 1 == line_length) {
741 *out++ = e0[uint8_t(src[i])];
743 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
745 *out++ = e0[uint8_t(src[i])];
746 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
750 *out++ = e0[uint8_t(src[i])];
751 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
756 t1 = uint8_t(src[i]);
757 t2 = uint8_t(src[i + 1]);
759 if (line_offset + 3 >= line_length) {
760 if (line_offset == line_length) {
763 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
764 *out++ = e2[(t2 & 0x0F) << 2];
766 }
else if (line_offset + 1 == line_length) {
769 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
770 *out++ = e2[(t2 & 0x0F) << 2];
772 }
else if (line_offset + 2 == line_length) {
774 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
776 *out++ = e2[(t2 & 0x0F) << 2];
778 }
else if (line_offset + 3 == line_length) {
780 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
781 *out++ = e2[(t2 & 0x0F) << 2];
787 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
788 *out++ = e2[(t2 & 0x0F) << 2];
792 if (line_offset + 3 >= line_length) {
793 if (line_offset == line_length) {
796 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
797 *out++ = e2[(t2 & 0x0F) << 2];
798 }
else if (line_offset + 1 == line_length) {
801 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
802 *out++ = e2[(t2 & 0x0F) << 2];
803 }
else if (line_offset + 2 == line_length) {
805 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
807 *out++ = e2[(t2 & 0x0F) << 2];
810 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
811 *out++ = e2[(t2 & 0x0F) << 2];
816 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
817 *out++ = e2[(t2 & 0x0F) << 2];
821 return (
size_t)(out - dst);
827simdutf_unused
inline simdutf_constexpr23
size_t tail_encode_base64(
828 char *dst,
const char *src,
size_t srclen, base64_options options) {
829 return tail_encode_base64_impl(dst, src, srclen, options);
832template <
class InputPtr>
833simdutf_warn_unused simdutf_constexpr23
size_t
840 if (input[length - 1] ==
'=') {
842 if (length > 1 && input[length - 2] ==
'=') {
865 size_t actual_length = length - padding;
866 if (actual_length % 4 <= 1) {
867 return actual_length / 4 * 3;
871 return actual_length / 4 * 3 + (actual_length % 4) - 1;
878template <
class char_type>
879simdutf_warn_unused simdutf_constexpr23
size_t
883 for (
size_t i = 0; i < length; i++) {
884 count += (input[i] >
' ');
892 while (pos > 0 && padding < 2) {
893 char_type c = input[--pos];
896 }
else if (c >
' ') {
900 return ((count - padding) * 3) / 4;
903template <
typename char_type>
904simdutf_warn_unused simdutf_constexpr23 full_result
905base64_to_binary_details_impl(
906 const char_type *input,
size_t length,
char *output, base64_options options,
907 last_chunk_handling_options last_chunk_options)
noexcept {
908 const bool ignore_garbage =
909 (options == base64_options::base64_url_accept_garbage) ||
910 (options == base64_options::base64_default_accept_garbage) ||
911 (options == base64_options::base64_default_or_url_accept_garbage);
912 auto ri = simdutf::scalar::base64::find_end(input, length, options);
913 size_t equallocation = ri.equallocation;
914 size_t equalsigns = ri.equalsigns;
916 size_t full_input_length = ri.full_input_length;
918 if (!ignore_garbage && equalsigns > 0) {
919 return {INVALID_BASE64_CHARACTER, equallocation, 0,
true};
921 return {SUCCESS, full_input_length, 0};
923 full_result r = scalar::base64::base64_tail_decode(
924 output, input, length, equalsigns, options, last_chunk_options);
925 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
926 full_input_length, last_chunk_options);
927 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
928 equalsigns > 0 && !ignore_garbage) {
930 if ((r.output_count % 3 == 0) ||
931 ((r.output_count % 3) + 1 + equalsigns != 4)) {
932 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count,
true};
939 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
940 r.input_count < full_input_length) {
942 while (r.input_count < full_input_length &&
948 if (r.input_count < full_input_length) {
949 while (r.input_count > 0 &&
958template <
typename char_type>
959simdutf_constexpr23 simdutf_warn_unused full_result
960base64_to_binary_details_safe_impl(
961 const char_type *input,
size_t length,
char *output,
size_t outlen,
962 base64_options options,
963 last_chunk_handling_options last_chunk_options)
noexcept {
964 const bool ignore_garbage =
965 (options == base64_options::base64_url_accept_garbage) ||
966 (options == base64_options::base64_default_accept_garbage) ||
967 (options == base64_options::base64_default_or_url_accept_garbage);
968 auto ri = simdutf::scalar::base64::find_end(input, length, options);
969 size_t equallocation = ri.equallocation;
970 size_t equalsigns = ri.equalsigns;
972 size_t full_input_length = ri.full_input_length;
974 if (!ignore_garbage && equalsigns > 0) {
975 return {INVALID_BASE64_CHARACTER, equallocation, 0};
977 return {SUCCESS, full_input_length, 0};
979 full_result r = scalar::base64::base64_tail_decode_safe(
980 output, outlen, input, length, equalsigns, options, last_chunk_options);
981 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
982 full_input_length, last_chunk_options);
983 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
984 equalsigns > 0 && !ignore_garbage) {
986 if ((r.output_count % 3 == 0) ||
987 ((r.output_count % 3) + 1 + equalsigns != 4)) {
988 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
996 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
997 r.input_count < full_input_length) {
999 while (r.input_count < full_input_length &&
1005 if (r.input_count < full_input_length) {
1006 while (r.input_count > 0 &&
1015simdutf_warn_unused simdutf_constexpr23
size_t
1026 ((options & base64_url) == 0) ^
1027 ((options & base64_reverse_padding) == base64_reverse_padding);
1029 return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
1031 return (length + 2) / 3 *
1035simdutf_warn_unused simdutf_constexpr23
size_t
1037 size_t line_length)
noexcept {
1041 size_t base64_length =
1042 scalar::base64::base64_length_from_binary(length, options);
1043 if (line_length < 4) {
1048 size_t lines = base64_length / line_length;
1049 if (base64_length % line_length != 0) {
1052 return base64_length + lines - 1;
1060template <
typename char_type>
1061simdutf_warn_unused
size_t prefix_length(
size_t count,
1062 simdutf::base64_options options,
1063 const char_type *input,
1064 size_t length)
noexcept {
1066 while (i < length && is_ignorable(input[i], options)) {
1072 for (; i < length; i++) {
1073 if (is_ignorable(input[i], options)) {
1082 simdutf_log_assert(
false,
"You never get here");
helpers placed in namespace detail are not a part of the public API
simdutf_warn_unused simdutf_constexpr23 size_t base64_length_from_binary(size_t length, base64_options options=base64_default) noexcept
Provide the base64 length in bytes given the length of a binary input.
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool base64_ignorable(char input, base64_options options=base64_default) noexcept
Check if a character is an ignorable base64 character.
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 const char * find(const char *start, const char *end, char character) noexcept
Find the first occurrence of a character in a string.
constexpr size_t default_line_length
default line length for base64 encoding with lines
simdutf_warn_unused size_t maximal_binary_length_from_base64(const char *input, size_t length) noexcept
Provide the maximal binary length in bytes given the base64 input.
simdutf_warn_unused size_t binary_length_from_base64(const char *input, size_t length) noexcept
Compute the binary length from a base64 input.
simdutf_warn_unused simdutf_constexpr23 size_t base64_length_from_binary_with_lines(size_t length, base64_options options=base64_default, size_t line_length=default_line_length) noexcept
Provide the base64 length in bytes given the length of a binary input, taking into account line break...