1#ifndef SIMDUTF_BASE64_H
2#define SIMDUTF_BASE64_H
17template <
class char_type>
bool is_ascii_white_space(char_type c) {
18 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f';
21template <
class char_type> simdutf_constexpr23
bool is_eight_byte(char_type c) {
22 if simdutf_constexpr (
sizeof(char_type) == 1) {
25 return uint8_t(c) == c;
28template <
class char_type>
29simdutf_constexpr23
bool is_ignorable(char_type c,
30 simdutf::base64_options options) {
31 const uint8_t *to_base64 =
32 (options & base64_default_or_url)
33 ? tables::base64::to_base64_default_or_url_value
34 : ((options & base64_url) ? tables::base64::to_base64_url_value
35 : tables::base64::to_base64_value);
36 const bool ignore_garbage =
37 (options == base64_options::base64_url_accept_garbage) ||
38 (options == base64_options::base64_default_accept_garbage) ||
39 (options == base64_options::base64_default_or_url_accept_garbage);
40 uint8_t code = to_base64[uint8_t(c)];
41 if (is_eight_byte(c) && code <= 63) {
44 if (is_eight_byte(c) && code == 64) {
47 return ignore_garbage;
49template <
class char_type>
50simdutf_constexpr23
bool is_base64(char_type c,
51 simdutf::base64_options options) {
52 const uint8_t *to_base64 =
53 (options & base64_default_or_url)
54 ? tables::base64::to_base64_default_or_url_value
55 : ((options & base64_url) ? tables::base64::to_base64_url_value
56 : tables::base64::to_base64_value);
57 uint8_t code = to_base64[uint8_t(c)];
58 if (is_eight_byte(c) && code <= 63) {
64template <
class char_type>
65simdutf_constexpr23
bool is_base64_or_padding(char_type c,
66 simdutf::base64_options options) {
67 const uint8_t *to_base64 =
68 (options & base64_default_or_url)
69 ? tables::base64::to_base64_default_or_url_value
70 : ((options & base64_url) ? tables::base64::to_base64_url_value
71 : tables::base64::to_base64_value);
75 uint8_t code = to_base64[uint8_t(c)];
76 if (is_eight_byte(c) && code <= 63) {
82template <
class char_type>
83bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) {
84 return is_ignorable(c, options) || c ==
'=';
91 size_t full_input_length;
100template <
class char_type>
101simdutf_constexpr23 reduced_input find_end(
const char_type *src,
size_t srclen,
102 simdutf::base64_options options) {
103 const uint8_t *to_base64 =
104 (options & base64_default_or_url)
105 ? tables::base64::to_base64_default_or_url_value
106 : ((options & base64_url) ? tables::base64::to_base64_url_value
107 : tables::base64::to_base64_value);
108 const bool ignore_garbage =
109 (options == base64_options::base64_url_accept_garbage) ||
110 (options == base64_options::base64_default_accept_garbage) ||
111 (options == base64_options::base64_default_or_url_accept_garbage);
113 size_t equalsigns = 0;
116 size_t full_input_length = srclen;
118 while (!ignore_garbage && srclen > 0 &&
119 scalar::base64::is_eight_byte(src[srclen - 1]) &&
120 to_base64[uint8_t(src[srclen - 1])] == 64) {
123 size_t equallocation =
125 if (ignore_garbage) {
128 auto it = simdutf::find(src, src + srclen,
'=');
129 if (it != src + srclen) {
130 equallocation = it - src;
132 srclen = equallocation;
133 full_input_length = equallocation + 1;
135 return {equalsigns, equallocation, srclen, full_input_length};
137 if (!ignore_garbage && srclen > 0 && src[srclen - 1] ==
'=') {
139 equallocation = srclen - 1;
143 while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
144 to_base64[uint8_t(src[srclen - 1])] == 64) {
147 if (srclen > 0 && src[srclen - 1] ==
'=') {
149 equallocation = srclen - 1;
154 return {equalsigns, equallocation, srclen, full_input_length};
161template <
bool check_capacity,
class char_type>
162simdutf_constexpr23 full_result base64_tail_decode_impl(
163 char *dst,
size_t outlen,
const char_type *src,
size_t length,
164 size_t padding_characters,
166 base64_options options, last_chunk_handling_options last_chunk_options) {
167 char *dstend = dst + outlen;
171 const uint8_t *to_base64 =
172 (options & base64_default_or_url)
173 ? tables::base64::to_base64_default_or_url_value
174 : ((options & base64_url) ? tables::base64::to_base64_url_value
175 : tables::base64::to_base64_value);
177 (options & base64_default_or_url)
178 ? tables::base64::base64_default_or_url::d0
179 : ((options & base64_url) ? tables::base64::base64_url::d0
180 : tables::base64::base64_default::d0);
182 (options & base64_default_or_url)
183 ? tables::base64::base64_default_or_url::d1
184 : ((options & base64_url) ? tables::base64::base64_url::d1
185 : tables::base64::base64_default::d1);
187 (options & base64_default_or_url)
188 ? tables::base64::base64_default_or_url::d2
189 : ((options & base64_url) ? tables::base64::base64_url::d2
190 : tables::base64::base64_default::d2);
192 (options & base64_default_or_url)
193 ? tables::base64::base64_default_or_url::d3
194 : ((options & base64_url) ? tables::base64::base64_url::d3
195 : tables::base64::base64_default::d3);
196 const bool ignore_garbage =
197 (options == base64_options::base64_url_accept_garbage) ||
198 (options == base64_options::base64_default_accept_garbage) ||
199 (options == base64_options::base64_default_or_url_accept_garbage);
201 const char_type *srcend = src + length;
202 const char_type *srcinit = src;
203 const char *dstinit = dst;
209 while (srcend - src >= 4 && is_eight_byte(src[0]) &&
210 is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
211 is_eight_byte(src[3]) &&
212 (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
213 d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
214 if (check_capacity && dstend - dst < 3) {
215 return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
216 size_t(dst - dstinit)};
218 *dst++ =
static_cast<char>(x & 0xFF);
219 *dst++ =
static_cast<char>((x >> 8) & 0xFF);
220 *dst++ =
static_cast<char>((x >> 16) & 0xFF);
223 const char_type *srccur = src;
228 if (ignore_garbage && src + 4 <= srcend) {
229 char_type c0 = src[0];
230 char_type c1 = src[1];
231 char_type c2 = src[2];
232 char_type c3 = src[3];
234 uint8_t code0 = to_base64[uint8_t(c0)];
235 uint8_t code1 = to_base64[uint8_t(c1)];
236 uint8_t code2 = to_base64[uint8_t(c2)];
237 uint8_t code3 = to_base64[uint8_t(c3)];
240 idx += (is_eight_byte(c0) && code0 <= 63);
242 idx += (is_eight_byte(c1) && code1 <= 63);
244 idx += (is_eight_byte(c2) && code2 <= 63);
246 idx += (is_eight_byte(c3) && code3 <= 63);
250 while ((idx < 4) && (src < srcend)) {
253 uint8_t code = to_base64[uint8_t(c)];
254 buffer[idx] = uint8_t(code);
255 if (is_eight_byte(c) && code <= 63) {
257 }
else if (!ignore_garbage &&
258 (code > 64 || !scalar::base64::is_eight_byte(c))) {
259 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
260 size_t(dst - dstinit)};
267 simdutf_log_assert(idx < 4,
"idx should be less than 4");
270 if (!ignore_garbage && (idx + padding_characters > 4)) {
271 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
272 size_t(dst - dstinit),
true};
279 if (!ignore_garbage &&
280 last_chunk_options == last_chunk_handling_options::loose &&
281 (idx >= 2) && padding_characters > 0 &&
282 ((idx + padding_characters) & 3) != 0) {
283 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
284 size_t(dst - dstinit),
true};
290 if (!ignore_garbage &&
291 last_chunk_options == last_chunk_handling_options::strict &&
292 (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
294 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
295 size_t(dst - dstinit),
true};
300 if ((last_chunk_options ==
301 last_chunk_handling_options::stop_before_partial &&
302 (padding_characters + idx < 4) && (idx != 0) &&
303 (idx >= 2 || padding_characters == 0)) ||
304 (last_chunk_options ==
305 last_chunk_handling_options::only_full_chunks &&
306 (idx >= 2 || padding_characters == 0))) {
310 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
313 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
314 (uint32_t(buffer[1]) << 2 * 6);
315 if (!ignore_garbage &&
316 (last_chunk_options == last_chunk_handling_options::strict) &&
318 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
319 size_t(dst - dstinit)};
321 if (check_capacity && dstend - dst < 1) {
322 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
323 size_t(dst - dstinit)};
325 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
326 }
else if (idx == 3) {
327 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
328 (uint32_t(buffer[1]) << 2 * 6) +
329 (uint32_t(buffer[2]) << 1 * 6);
330 if (!ignore_garbage &&
331 (last_chunk_options == last_chunk_handling_options::strict) &&
333 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
334 size_t(dst - dstinit)};
336 if (check_capacity && dstend - dst < 2) {
337 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
338 size_t(dst - dstinit)};
340 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
341 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
342 }
else if (!ignore_garbage && idx == 1 &&
343 (!is_partial(last_chunk_options) ||
344 (is_partial(last_chunk_options) &&
345 padding_characters > 0))) {
346 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
347 size_t(dst - dstinit)};
348 }
else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
349 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
350 size_t(dst - dstinit),
true};
352 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
355 if (check_capacity && dstend - dst < 3) {
356 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
357 size_t(dst - dstinit)};
360 (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
361 (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
362 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
363 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
364 *dst++ =
static_cast<char>(triple & 0xFF);
368template <
class char_type>
369simdutf_constexpr23 full_result base64_tail_decode(
370 char *dst,
const char_type *src,
size_t length,
371 size_t padding_characters,
373 base64_options options, last_chunk_handling_options last_chunk_options) {
374 return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
375 options, last_chunk_options);
382template <
class char_type>
383simdutf_constexpr23 full_result base64_tail_decode_safe(
384 char *dst,
size_t outlen,
const char_type *src,
size_t length,
385 size_t padding_characters,
387 base64_options options, last_chunk_handling_options last_chunk_options) {
388 return base64_tail_decode_impl<true>(dst, outlen, src, length,
389 padding_characters, options,
393inline simdutf_constexpr23 full_result
394patch_tail_result(full_result r,
size_t previous_input,
size_t previous_output,
395 size_t equallocation,
size_t full_input_length,
396 last_chunk_handling_options last_chunk_options) {
397 r.input_count += previous_input;
398 r.output_count += previous_output;
399 if (r.padding_error) {
400 r.input_count = equallocation;
403 if (r.error == error_code::SUCCESS) {
404 if (!is_partial(last_chunk_options)) {
407 r.input_count = full_input_length;
408 }
else if (r.output_count % 3 != 0) {
409 r.input_count = full_input_length;
417template <
bool use_lines = false>
418simdutf_constexpr23
size_t tail_encode_base64_impl(
419 char *dst,
const char *src,
size_t srclen, base64_options options,
420 size_t line_length = simdutf::default_line_length,
size_t line_offset = 0) {
421 if simdutf_constexpr (use_lines) {
424 if (line_length < 4) {
427 simdutf_log_assert(line_offset <= line_length,
428 "line_offset should be less than line_length");
439 ((options & base64_url) == 0) ^
440 ((options & base64_reverse_padding) == base64_reverse_padding);
443 const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0
444 : tables::base64::base64_default::e0;
445 const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1
446 : tables::base64::base64_default::e1;
447 const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2
448 : tables::base64::base64_default::e2;
452 for (; i + 2 < srclen; i += 3) {
453 t1 = uint8_t(src[i]);
454 t2 = uint8_t(src[i + 1]);
455 t3 = uint8_t(src[i + 2]);
456 if simdutf_constexpr (use_lines) {
457 if (line_offset + 3 >= line_length) {
458 if (line_offset == line_length) {
461 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
462 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
465 }
else if (line_offset + 1 == line_length) {
468 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
469 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
472 }
else if (line_offset + 2 == line_length) {
474 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
476 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
479 }
else if (line_offset + 3 == line_length) {
481 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
482 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
489 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
490 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
496 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
497 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
501 switch (srclen - i) {
505 t1 = uint8_t(src[i]);
506 if simdutf_constexpr (use_lines) {
508 if (line_offset + 3 >= line_length) {
509 if (line_offset == line_length) {
512 *out++ = e1[(t1 & 0x03) << 4];
515 }
else if (line_offset + 1 == line_length) {
518 *out++ = e1[(t1 & 0x03) << 4];
521 }
else if (line_offset + 2 == line_length) {
523 *out++ = e1[(t1 & 0x03) << 4];
527 }
else if (line_offset + 3 == line_length) {
529 *out++ = e1[(t1 & 0x03) << 4];
536 *out++ = e1[(t1 & 0x03) << 4];
541 if (line_offset + 2 >= line_length) {
542 if (line_offset == line_length) {
544 *out++ = e0[uint8_t(src[i])];
545 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
546 }
else if (line_offset + 1 == line_length) {
547 *out++ = e0[uint8_t(src[i])];
549 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
551 *out++ = e0[uint8_t(src[i])];
552 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
556 *out++ = e0[uint8_t(src[i])];
557 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
562 *out++ = e1[(t1 & 0x03) << 4];
570 t1 = uint8_t(src[i]);
571 t2 = uint8_t(src[i + 1]);
572 if simdutf_constexpr (use_lines) {
574 if (line_offset + 3 >= line_length) {
575 if (line_offset == line_length) {
578 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
579 *out++ = e2[(t2 & 0x0F) << 2];
581 }
else if (line_offset + 1 == line_length) {
584 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
585 *out++ = e2[(t2 & 0x0F) << 2];
587 }
else if (line_offset + 2 == line_length) {
589 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
591 *out++ = e2[(t2 & 0x0F) << 2];
593 }
else if (line_offset + 3 == line_length) {
595 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
596 *out++ = e2[(t2 & 0x0F) << 2];
602 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
603 *out++ = e2[(t2 & 0x0F) << 2];
607 if (line_offset + 3 >= line_length) {
608 if (line_offset == line_length) {
611 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
612 *out++ = e2[(t2 & 0x0F) << 2];
613 }
else if (line_offset + 1 == line_length) {
616 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
617 *out++ = e2[(t2 & 0x0F) << 2];
618 }
else if (line_offset + 2 == line_length) {
620 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
622 *out++ = e2[(t2 & 0x0F) << 2];
625 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
626 *out++ = e2[(t2 & 0x0F) << 2];
631 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
632 *out++ = e2[(t2 & 0x0F) << 2];
637 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
638 *out++ = e2[(t2 & 0x0F) << 2];
644 return (
size_t)(out - dst);
649inline simdutf_constexpr23
size_t tail_encode_base64(
char *dst,
const char *src,
651 base64_options options) {
652 return tail_encode_base64_impl(dst, src, srclen, options);
655template <
class InputPtr>
656simdutf_warn_unused simdutf_constexpr23
size_t
657maximal_binary_length_from_base64(InputPtr input,
size_t length)
noexcept {
663 if (input[length - 1] ==
'=') {
665 if (length > 1 && input[length - 2] ==
'=') {
688 size_t actual_length = length - padding;
689 if (actual_length % 4 <= 1) {
690 return actual_length / 4 * 3;
694 return actual_length / 4 * 3 + (actual_length % 4) - 1;
697template <
typename char_type>
698simdutf_warn_unused simdutf_constexpr23 full_result
699base64_to_binary_details_impl(
700 const char_type *input,
size_t length,
char *output, base64_options options,
701 last_chunk_handling_options last_chunk_options)
noexcept {
702 const bool ignore_garbage =
703 (options == base64_options::base64_url_accept_garbage) ||
704 (options == base64_options::base64_default_accept_garbage) ||
705 (options == base64_options::base64_default_or_url_accept_garbage);
706 auto ri = simdutf::scalar::base64::find_end(input, length, options);
707 size_t equallocation = ri.equallocation;
708 size_t equalsigns = ri.equalsigns;
710 size_t full_input_length = ri.full_input_length;
712 if (!ignore_garbage && equalsigns > 0) {
713 return {INVALID_BASE64_CHARACTER, equallocation, 0};
715 return {SUCCESS, full_input_length, 0};
717 full_result r = scalar::base64::base64_tail_decode(
718 output, input, length, equalsigns, options, last_chunk_options);
719 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
720 full_input_length, last_chunk_options);
721 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
722 equalsigns > 0 && !ignore_garbage) {
724 if ((r.output_count % 3 == 0) ||
725 ((r.output_count % 3) + 1 + equalsigns != 4)) {
726 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
733 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
734 r.input_count < full_input_length) {
736 while (r.input_count < full_input_length &&
737 base64_ignorable(*(input + r.input_count), options)) {
742 if (r.input_count < full_input_length) {
743 while (r.input_count > 0 &&
744 base64_ignorable(*(input + r.input_count - 1), options)) {
752template <
typename char_type>
753simdutf_constexpr23 simdutf_warn_unused full_result
754base64_to_binary_details_safe_impl(
755 const char_type *input,
size_t length,
char *output,
size_t outlen,
756 base64_options options,
757 last_chunk_handling_options last_chunk_options)
noexcept {
758 const bool ignore_garbage =
759 (options == base64_options::base64_url_accept_garbage) ||
760 (options == base64_options::base64_default_accept_garbage) ||
761 (options == base64_options::base64_default_or_url_accept_garbage);
762 auto ri = simdutf::scalar::base64::find_end(input, length, options);
763 size_t equallocation = ri.equallocation;
764 size_t equalsigns = ri.equalsigns;
766 size_t full_input_length = ri.full_input_length;
768 if (!ignore_garbage && equalsigns > 0) {
769 return {INVALID_BASE64_CHARACTER, equallocation, 0};
771 return {SUCCESS, full_input_length, 0};
773 full_result r = scalar::base64::base64_tail_decode_safe(
774 output, outlen, input, length, equalsigns, options, last_chunk_options);
775 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
776 full_input_length, last_chunk_options);
777 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
778 equalsigns > 0 && !ignore_garbage) {
780 if ((r.output_count % 3 == 0) ||
781 ((r.output_count % 3) + 1 + equalsigns != 4)) {
782 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
790 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
791 r.input_count < full_input_length) {
793 while (r.input_count < full_input_length &&
794 base64_ignorable(*(input + r.input_count), options)) {
799 if (r.input_count < full_input_length) {
800 while (r.input_count > 0 &&
801 base64_ignorable(*(input + r.input_count - 1), options)) {
809simdutf_warn_unused simdutf_constexpr23
size_t
810base64_length_from_binary(
size_t length, base64_options options)
noexcept {
820 ((options & base64_url) == 0) ^
821 ((options & base64_reverse_padding) == base64_reverse_padding);
823 return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
825 return (length + 2) / 3 *
829simdutf_warn_unused simdutf_constexpr23
size_t
830base64_length_from_binary_with_lines(
size_t length, base64_options options,
831 size_t line_length)
noexcept {
835 size_t base64_length =
836 scalar::base64::base64_length_from_binary(length, options);
837 if (line_length < 4) {
841 (base64_length + line_length - 1) / line_length;
842 return base64_length + lines - 1;
850template <
typename char_type>
851simdutf_warn_unused
size_t prefix_length(
size_t count,
852 simdutf::base64_options options,
853 const char_type *input,
854 size_t length)
noexcept {
856 while (i < length && is_ignorable(input[i], options)) {
862 for (; i < length; i++) {
863 if (is_ignorable(input[i], options)) {
872 simdutf_log_assert(
false,
"You never get here");