1#ifndef SIMDUTF_BASE64_H
2#define SIMDUTF_BASE64_H
15template <
class char_type>
bool is_ascii_white_space(char_type c) {
16 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f';
19template <
class char_type> simdutf_constexpr23
bool is_eight_byte(char_type c) {
20 if constexpr (
sizeof(char_type) == 1) {
23 return uint8_t(c) == c;
26template <
class char_type>
27simdutf_constexpr23
bool is_ignorable(char_type c,
28 simdutf::base64_options options) {
29 const uint8_t *to_base64 =
30 (options & base64_default_or_url)
31 ? tables::base64::to_base64_default_or_url_value
32 : ((options & base64_url) ? tables::base64::to_base64_url_value
33 : tables::base64::to_base64_value);
34 const bool ignore_garbage =
35 (options == base64_options::base64_url_accept_garbage) ||
36 (options == base64_options::base64_default_accept_garbage) ||
37 (options == base64_options::base64_default_or_url_accept_garbage);
38 uint8_t code = to_base64[uint8_t(c)];
39 if (is_eight_byte(c) && code <= 63) {
42 if (is_eight_byte(c) && code == 64) {
45 return ignore_garbage;
47template <
class char_type>
48simdutf_constexpr23
bool is_base64(char_type c,
49 simdutf::base64_options options) {
50 const uint8_t *to_base64 =
51 (options & base64_default_or_url)
52 ? tables::base64::to_base64_default_or_url_value
53 : ((options & base64_url) ? tables::base64::to_base64_url_value
54 : tables::base64::to_base64_value);
55 uint8_t code = to_base64[uint8_t(c)];
56 if (is_eight_byte(c) && code <= 63) {
62template <
class char_type>
63simdutf_constexpr23
bool is_base64_or_padding(char_type c,
64 simdutf::base64_options options) {
65 const uint8_t *to_base64 =
66 (options & base64_default_or_url)
67 ? tables::base64::to_base64_default_or_url_value
68 : ((options & base64_url) ? tables::base64::to_base64_url_value
69 : tables::base64::to_base64_value);
73 uint8_t code = to_base64[uint8_t(c)];
74 if (is_eight_byte(c) && code <= 63) {
80template <
class char_type>
81bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) {
82 return is_ignorable(c, options) || c ==
'=';
89 size_t full_input_length;
98template <
class char_type>
99simdutf_constexpr23 reduced_input find_end(
const char_type *src,
size_t srclen,
100 simdutf::base64_options options) {
101 const uint8_t *to_base64 =
102 (options & base64_default_or_url)
103 ? tables::base64::to_base64_default_or_url_value
104 : ((options & base64_url) ? tables::base64::to_base64_url_value
105 : tables::base64::to_base64_value);
106 const bool ignore_garbage =
107 (options == base64_options::base64_url_accept_garbage) ||
108 (options == base64_options::base64_default_accept_garbage) ||
109 (options == base64_options::base64_default_or_url_accept_garbage);
111 size_t equalsigns = 0;
114 size_t full_input_length = srclen;
116 while (!ignore_garbage && srclen > 0 &&
117 scalar::base64::is_eight_byte(src[srclen - 1]) &&
118 to_base64[uint8_t(src[srclen - 1])] == 64) {
121 size_t equallocation =
123 if (ignore_garbage) {
126 auto it = simdutf::find(src, src + srclen,
'=');
127 if (it != src + srclen) {
128 equallocation = it - src;
130 srclen = equallocation;
131 full_input_length = equallocation + 1;
133 return {equalsigns, equallocation, srclen, full_input_length};
135 if (!ignore_garbage && srclen > 0 && src[srclen - 1] ==
'=') {
137 equallocation = srclen - 1;
141 while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
142 to_base64[uint8_t(src[srclen - 1])] == 64) {
145 if (srclen > 0 && src[srclen - 1] ==
'=') {
147 equallocation = srclen - 1;
152 return {equalsigns, equallocation, srclen, full_input_length};
159template <
bool check_capacity,
class char_type>
160simdutf_constexpr23 full_result base64_tail_decode_impl(
161 char *dst,
size_t outlen,
const char_type *src,
size_t length,
162 size_t padding_characters,
164 base64_options options, last_chunk_handling_options last_chunk_options) {
165 char *dstend = dst + outlen;
169 const uint8_t *to_base64 =
170 (options & base64_default_or_url)
171 ? tables::base64::to_base64_default_or_url_value
172 : ((options & base64_url) ? tables::base64::to_base64_url_value
173 : tables::base64::to_base64_value);
175 (options & base64_default_or_url)
176 ? tables::base64::base64_default_or_url::d0
177 : ((options & base64_url) ? tables::base64::base64_url::d0
178 : tables::base64::base64_default::d0);
180 (options & base64_default_or_url)
181 ? tables::base64::base64_default_or_url::d1
182 : ((options & base64_url) ? tables::base64::base64_url::d1
183 : tables::base64::base64_default::d1);
185 (options & base64_default_or_url)
186 ? tables::base64::base64_default_or_url::d2
187 : ((options & base64_url) ? tables::base64::base64_url::d2
188 : tables::base64::base64_default::d2);
190 (options & base64_default_or_url)
191 ? tables::base64::base64_default_or_url::d3
192 : ((options & base64_url) ? tables::base64::base64_url::d3
193 : tables::base64::base64_default::d3);
194 const bool ignore_garbage =
195 (options == base64_options::base64_url_accept_garbage) ||
196 (options == base64_options::base64_default_accept_garbage) ||
197 (options == base64_options::base64_default_or_url_accept_garbage);
199 const char_type *srcend = src + length;
200 const char_type *srcinit = src;
201 const char *dstinit = dst;
207 while (srcend - src >= 4 && is_eight_byte(src[0]) &&
208 is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
209 is_eight_byte(src[3]) &&
210 (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
211 d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
212 if (check_capacity && dstend - dst < 3) {
213 return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
214 size_t(dst - dstinit)};
216 *dst++ =
static_cast<char>(x & 0xFF);
217 *dst++ =
static_cast<char>((x >> 8) & 0xFF);
218 *dst++ =
static_cast<char>((x >> 16) & 0xFF);
221 const char_type *srccur = src;
226 if (ignore_garbage && src + 4 <= srcend) {
227 char_type c0 = src[0];
228 char_type c1 = src[1];
229 char_type c2 = src[2];
230 char_type c3 = src[3];
232 uint8_t code0 = to_base64[uint8_t(c0)];
233 uint8_t code1 = to_base64[uint8_t(c1)];
234 uint8_t code2 = to_base64[uint8_t(c2)];
235 uint8_t code3 = to_base64[uint8_t(c3)];
238 idx += (is_eight_byte(c0) && code0 <= 63);
240 idx += (is_eight_byte(c1) && code1 <= 63);
242 idx += (is_eight_byte(c2) && code2 <= 63);
244 idx += (is_eight_byte(c3) && code3 <= 63);
248 while ((idx < 4) && (src < srcend)) {
251 uint8_t code = to_base64[uint8_t(c)];
252 buffer[idx] = uint8_t(code);
253 if (is_eight_byte(c) && code <= 63) {
255 }
else if (!ignore_garbage &&
256 (code > 64 || !scalar::base64::is_eight_byte(c))) {
257 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
258 size_t(dst - dstinit)};
265 simdutf_log_assert(idx < 4,
"idx should be less than 4");
268 if (!ignore_garbage && (idx + padding_characters > 4)) {
269 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
270 size_t(dst - dstinit),
true};
277 if (!ignore_garbage &&
278 last_chunk_options == last_chunk_handling_options::loose &&
279 (idx >= 2) && padding_characters > 0 &&
280 ((idx + padding_characters) & 3) != 0) {
281 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
282 size_t(dst - dstinit),
true};
288 if (!ignore_garbage &&
289 last_chunk_options == last_chunk_handling_options::strict &&
290 (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
292 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
293 size_t(dst - dstinit),
true};
298 if ((last_chunk_options ==
299 last_chunk_handling_options::stop_before_partial &&
300 (padding_characters + idx < 4) && (idx != 0) &&
301 (idx >= 2 || padding_characters == 0)) ||
302 (last_chunk_options ==
303 last_chunk_handling_options::only_full_chunks &&
304 (idx >= 2 || padding_characters == 0))) {
308 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
311 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
312 (uint32_t(buffer[1]) << 2 * 6);
313 if (!ignore_garbage &&
314 (last_chunk_options == last_chunk_handling_options::strict) &&
316 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
317 size_t(dst - dstinit)};
319 if (check_capacity && dstend - dst < 1) {
320 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
321 size_t(dst - dstinit)};
323 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
324 }
else if (idx == 3) {
325 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
326 (uint32_t(buffer[1]) << 2 * 6) +
327 (uint32_t(buffer[2]) << 1 * 6);
328 if (!ignore_garbage &&
329 (last_chunk_options == last_chunk_handling_options::strict) &&
331 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
332 size_t(dst - dstinit)};
334 if (check_capacity && dstend - dst < 2) {
335 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
336 size_t(dst - dstinit)};
338 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
339 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
340 }
else if (!ignore_garbage && idx == 1 &&
341 (!is_partial(last_chunk_options) ||
342 (is_partial(last_chunk_options) &&
343 padding_characters > 0))) {
344 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
345 size_t(dst - dstinit)};
346 }
else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
347 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
348 size_t(dst - dstinit),
true};
350 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
353 if (check_capacity && dstend - dst < 3) {
354 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
355 size_t(dst - dstinit)};
358 (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
359 (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
360 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
361 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
362 *dst++ =
static_cast<char>(triple & 0xFF);
366template <
class char_type>
367simdutf_constexpr23 full_result base64_tail_decode(
368 char *dst,
const char_type *src,
size_t length,
369 size_t padding_characters,
371 base64_options options, last_chunk_handling_options last_chunk_options) {
372 return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
373 options, last_chunk_options);
380template <
class char_type>
381simdutf_constexpr23 full_result base64_tail_decode_safe(
382 char *dst,
size_t outlen,
const char_type *src,
size_t length,
383 size_t padding_characters,
385 base64_options options, last_chunk_handling_options last_chunk_options) {
386 return base64_tail_decode_impl<true>(dst, outlen, src, length,
387 padding_characters, options,
391inline simdutf_constexpr23 full_result
392patch_tail_result(full_result r,
size_t previous_input,
size_t previous_output,
393 size_t equallocation,
size_t full_input_length,
394 last_chunk_handling_options last_chunk_options) {
395 r.input_count += previous_input;
396 r.output_count += previous_output;
397 if (r.padding_error) {
398 r.input_count = equallocation;
401 if (r.error == error_code::SUCCESS) {
402 if (!is_partial(last_chunk_options)) {
405 r.input_count = full_input_length;
406 }
else if (r.output_count % 3 != 0) {
407 r.input_count = full_input_length;
415template <
bool use_lines = false>
416simdutf_constexpr23
size_t tail_encode_base64_impl(
417 char *dst,
const char *src,
size_t srclen, base64_options options,
418 size_t line_length = simdutf::default_line_length,
size_t line_offset = 0) {
419 if constexpr (use_lines) {
422 if (line_length < 4) {
425 simdutf_log_assert(line_offset <= line_length,
426 "line_offset should be less than line_length");
437 ((options & base64_url) == 0) ^
438 ((options & base64_reverse_padding) == base64_reverse_padding);
441 const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0
442 : tables::base64::base64_default::e0;
443 const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1
444 : tables::base64::base64_default::e1;
445 const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2
446 : tables::base64::base64_default::e2;
450 for (; i + 2 < srclen; i += 3) {
451 t1 = uint8_t(src[i]);
452 t2 = uint8_t(src[i + 1]);
453 t3 = uint8_t(src[i + 2]);
454 if constexpr (use_lines) {
455 if (line_offset + 3 >= line_length) {
456 if (line_offset == line_length) {
459 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
460 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
463 }
else if (line_offset + 1 == line_length) {
466 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
467 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
470 }
else if (line_offset + 2 == line_length) {
472 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
474 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
477 }
else if (line_offset + 3 == line_length) {
479 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
480 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
487 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
488 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
494 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
495 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
499 switch (srclen - i) {
503 t1 = uint8_t(src[i]);
504 if constexpr (use_lines) {
506 if (line_offset + 3 >= line_length) {
507 if (line_offset == line_length) {
510 *out++ = e1[(t1 & 0x03) << 4];
513 }
else if (line_offset + 1 == line_length) {
516 *out++ = e1[(t1 & 0x03) << 4];
519 }
else if (line_offset + 2 == line_length) {
521 *out++ = e1[(t1 & 0x03) << 4];
525 }
else if (line_offset + 3 == line_length) {
527 *out++ = e1[(t1 & 0x03) << 4];
534 *out++ = e1[(t1 & 0x03) << 4];
539 if (line_offset + 2 >= line_length) {
540 if (line_offset == line_length) {
542 *out++ = e0[uint8_t(src[i])];
543 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
544 }
else if (line_offset + 1 == line_length) {
545 *out++ = e0[uint8_t(src[i])];
547 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
549 *out++ = e0[uint8_t(src[i])];
550 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
554 *out++ = e0[uint8_t(src[i])];
555 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
560 *out++ = e1[(t1 & 0x03) << 4];
568 t1 = uint8_t(src[i]);
569 t2 = uint8_t(src[i + 1]);
570 if constexpr (use_lines) {
572 if (line_offset + 3 >= line_length) {
573 if (line_offset == line_length) {
576 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
577 *out++ = e2[(t2 & 0x0F) << 2];
579 }
else if (line_offset + 1 == line_length) {
582 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
583 *out++ = e2[(t2 & 0x0F) << 2];
585 }
else if (line_offset + 2 == line_length) {
587 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
589 *out++ = e2[(t2 & 0x0F) << 2];
591 }
else if (line_offset + 3 == line_length) {
593 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
594 *out++ = e2[(t2 & 0x0F) << 2];
600 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
601 *out++ = e2[(t2 & 0x0F) << 2];
605 if (line_offset + 3 >= line_length) {
606 if (line_offset == line_length) {
609 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
610 *out++ = e2[(t2 & 0x0F) << 2];
611 }
else if (line_offset + 1 == line_length) {
614 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
615 *out++ = e2[(t2 & 0x0F) << 2];
616 }
else if (line_offset + 2 == line_length) {
618 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
620 *out++ = e2[(t2 & 0x0F) << 2];
623 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
624 *out++ = e2[(t2 & 0x0F) << 2];
629 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
630 *out++ = e2[(t2 & 0x0F) << 2];
635 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
636 *out++ = e2[(t2 & 0x0F) << 2];
642 return (
size_t)(out - dst);
647simdutf_unused
inline simdutf_constexpr23
size_t tail_encode_base64(
648 char *dst,
const char *src,
size_t srclen, base64_options options) {
649 return tail_encode_base64_impl(dst, src, srclen, options);
652template <
class InputPtr>
653simdutf_warn_unused simdutf_constexpr23
size_t
654maximal_binary_length_from_base64(InputPtr input,
size_t length)
noexcept {
660 if (input[length - 1] ==
'=') {
662 if (length > 1 && input[length - 2] ==
'=') {
685 size_t actual_length = length - padding;
686 if (actual_length % 4 <= 1) {
687 return actual_length / 4 * 3;
691 return actual_length / 4 * 3 + (actual_length % 4) - 1;
698template <
class char_type>
699simdutf_warn_unused simdutf_constexpr23
size_t
700binary_length_from_base64(
const char_type *input,
size_t length)
noexcept {
703 for (
size_t i = 0; i < length; i++) {
704 count += (input[i] >
' ');
712 while (pos > 0 && padding < 2) {
713 char_type c = input[--pos];
716 }
else if (c >
' ') {
720 return ((count - padding) * 3) / 4;
723template <
typename char_type>
724simdutf_warn_unused simdutf_constexpr23 full_result
725base64_to_binary_details_impl(
726 const char_type *input,
size_t length,
char *output, base64_options options,
727 last_chunk_handling_options last_chunk_options)
noexcept {
728 const bool ignore_garbage =
729 (options == base64_options::base64_url_accept_garbage) ||
730 (options == base64_options::base64_default_accept_garbage) ||
731 (options == base64_options::base64_default_or_url_accept_garbage);
732 auto ri = simdutf::scalar::base64::find_end(input, length, options);
733 size_t equallocation = ri.equallocation;
734 size_t equalsigns = ri.equalsigns;
736 size_t full_input_length = ri.full_input_length;
738 if (!ignore_garbage && equalsigns > 0) {
739 return {INVALID_BASE64_CHARACTER, equallocation, 0,
true};
741 return {SUCCESS, full_input_length, 0};
743 full_result r = scalar::base64::base64_tail_decode(
744 output, input, length, equalsigns, options, last_chunk_options);
745 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
746 full_input_length, last_chunk_options);
747 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
748 equalsigns > 0 && !ignore_garbage) {
750 if ((r.output_count % 3 == 0) ||
751 ((r.output_count % 3) + 1 + equalsigns != 4)) {
752 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count,
true};
759 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
760 r.input_count < full_input_length) {
762 while (r.input_count < full_input_length &&
763 base64_ignorable(*(input + r.input_count), options)) {
768 if (r.input_count < full_input_length) {
769 while (r.input_count > 0 &&
770 base64_ignorable(*(input + r.input_count - 1), options)) {
778template <
typename char_type>
779simdutf_constexpr23 simdutf_warn_unused full_result
780base64_to_binary_details_safe_impl(
781 const char_type *input,
size_t length,
char *output,
size_t outlen,
782 base64_options options,
783 last_chunk_handling_options last_chunk_options)
noexcept {
784 const bool ignore_garbage =
785 (options == base64_options::base64_url_accept_garbage) ||
786 (options == base64_options::base64_default_accept_garbage) ||
787 (options == base64_options::base64_default_or_url_accept_garbage);
788 auto ri = simdutf::scalar::base64::find_end(input, length, options);
789 size_t equallocation = ri.equallocation;
790 size_t equalsigns = ri.equalsigns;
792 size_t full_input_length = ri.full_input_length;
794 if (!ignore_garbage && equalsigns > 0) {
795 return {INVALID_BASE64_CHARACTER, equallocation, 0};
797 return {SUCCESS, full_input_length, 0};
799 full_result r = scalar::base64::base64_tail_decode_safe(
800 output, outlen, input, length, equalsigns, options, last_chunk_options);
801 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
802 full_input_length, last_chunk_options);
803 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
804 equalsigns > 0 && !ignore_garbage) {
806 if ((r.output_count % 3 == 0) ||
807 ((r.output_count % 3) + 1 + equalsigns != 4)) {
808 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
816 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
817 r.input_count < full_input_length) {
819 while (r.input_count < full_input_length &&
820 base64_ignorable(*(input + r.input_count), options)) {
825 if (r.input_count < full_input_length) {
826 while (r.input_count > 0 &&
827 base64_ignorable(*(input + r.input_count - 1), options)) {
835simdutf_warn_unused simdutf_constexpr23
size_t
836base64_length_from_binary(
size_t length, base64_options options)
noexcept {
846 ((options & base64_url) == 0) ^
847 ((options & base64_reverse_padding) == base64_reverse_padding);
849 return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
851 return (length + 2) / 3 *
855simdutf_warn_unused simdutf_constexpr23
size_t
856base64_length_from_binary_with_lines(
size_t length, base64_options options,
857 size_t line_length)
noexcept {
861 size_t base64_length =
862 scalar::base64::base64_length_from_binary(length, options);
863 if (line_length < 4) {
867 (base64_length + line_length - 1) / line_length;
868 return base64_length + lines - 1;
876template <
typename char_type>
877simdutf_warn_unused
size_t prefix_length(
size_t count,
878 simdutf::base64_options options,
879 const char_type *input,
880 size_t length)
noexcept {
882 while (i < length && is_ignorable(input[i], options)) {
888 for (; i < length; i++) {
889 if (is_ignorable(input[i], options)) {
898 simdutf_log_assert(
false,
"You never get here");