1#ifndef SIMDUTF_BASE64_H
2#define SIMDUTF_BASE64_H
16template <
class char_type>
bool is_ascii_white_space(char_type c) {
17 return c ==
' ' || c ==
'\t' || c ==
'\n' || c ==
'\r' || c ==
'\f';
20template <
class char_type> simdutf_constexpr23
bool is_eight_byte(char_type c) {
21 if constexpr (
sizeof(char_type) == 1) {
24 return uint8_t(c) == c;
27template <
class char_type>
28simdutf_constexpr23
bool is_ignorable(char_type c,
29 simdutf::base64_options options) {
30 const uint8_t *to_base64 =
31 (options & base64_default_or_url)
32 ? tables::base64::to_base64_default_or_url_value
33 : ((options & base64_url) ? tables::base64::to_base64_url_value
34 : tables::base64::to_base64_value);
35 const bool ignore_garbage =
36 (options == base64_options::base64_url_accept_garbage) ||
37 (options == base64_options::base64_default_accept_garbage) ||
38 (options == base64_options::base64_default_or_url_accept_garbage);
39 uint8_t code = to_base64[uint8_t(c)];
40 if (is_eight_byte(c) && code <= 63) {
43 if (is_eight_byte(c) && code == 64) {
46 return ignore_garbage;
48template <
class char_type>
49simdutf_constexpr23
bool is_base64(char_type c,
50 simdutf::base64_options options) {
51 const uint8_t *to_base64 =
52 (options & base64_default_or_url)
53 ? tables::base64::to_base64_default_or_url_value
54 : ((options & base64_url) ? tables::base64::to_base64_url_value
55 : tables::base64::to_base64_value);
56 uint8_t code = to_base64[uint8_t(c)];
57 if (is_eight_byte(c) && code <= 63) {
63template <
class char_type>
64simdutf_constexpr23
bool is_base64_or_padding(char_type c,
65 simdutf::base64_options options) {
66 const uint8_t *to_base64 =
67 (options & base64_default_or_url)
68 ? tables::base64::to_base64_default_or_url_value
69 : ((options & base64_url) ? tables::base64::to_base64_url_value
70 : tables::base64::to_base64_value);
74 uint8_t code = to_base64[uint8_t(c)];
75 if (is_eight_byte(c) && code <= 63) {
81template <
class char_type>
82bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) {
83 return is_ignorable(c, options) || c ==
'=';
90 size_t full_input_length;
99template <
class char_type>
100simdutf_constexpr23 reduced_input find_end(
const char_type *src,
size_t srclen,
101 simdutf::base64_options options) {
102 const uint8_t *to_base64 =
103 (options & base64_default_or_url)
104 ? tables::base64::to_base64_default_or_url_value
105 : ((options & base64_url) ? tables::base64::to_base64_url_value
106 : tables::base64::to_base64_value);
107 const bool ignore_garbage =
108 (options == base64_options::base64_url_accept_garbage) ||
109 (options == base64_options::base64_default_accept_garbage) ||
110 (options == base64_options::base64_default_or_url_accept_garbage);
112 size_t equalsigns = 0;
115 size_t full_input_length = srclen;
117 while (!ignore_garbage && srclen > 0 &&
118 scalar::base64::is_eight_byte(src[srclen - 1]) &&
119 to_base64[uint8_t(src[srclen - 1])] == 64) {
122 size_t equallocation =
124 if (ignore_garbage) {
127 auto it = simdutf::find(src, src + srclen,
'=');
128 if (it != src + srclen) {
129 equallocation = it - src;
131 srclen = equallocation;
132 full_input_length = equallocation + 1;
134 return {equalsigns, equallocation, srclen, full_input_length};
136 if (!ignore_garbage && srclen > 0 && src[srclen - 1] ==
'=') {
138 equallocation = srclen - 1;
142 while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
143 to_base64[uint8_t(src[srclen - 1])] == 64) {
146 if (srclen > 0 && src[srclen - 1] ==
'=') {
148 equallocation = srclen - 1;
153 return {equalsigns, equallocation, srclen, full_input_length};
160template <
bool check_capacity,
class char_type>
161simdutf_constexpr23 full_result base64_tail_decode_impl(
162 char *dst,
size_t outlen,
const char_type *src,
size_t length,
163 size_t padding_characters,
165 base64_options options, last_chunk_handling_options last_chunk_options) {
166 char *dstend = dst + outlen;
170 const uint8_t *to_base64 =
171 (options & base64_default_or_url)
172 ? tables::base64::to_base64_default_or_url_value
173 : ((options & base64_url) ? tables::base64::to_base64_url_value
174 : tables::base64::to_base64_value);
176 (options & base64_default_or_url)
177 ? tables::base64::base64_default_or_url::d0
178 : ((options & base64_url) ? tables::base64::base64_url::d0
179 : tables::base64::base64_default::d0);
181 (options & base64_default_or_url)
182 ? tables::base64::base64_default_or_url::d1
183 : ((options & base64_url) ? tables::base64::base64_url::d1
184 : tables::base64::base64_default::d1);
186 (options & base64_default_or_url)
187 ? tables::base64::base64_default_or_url::d2
188 : ((options & base64_url) ? tables::base64::base64_url::d2
189 : tables::base64::base64_default::d2);
191 (options & base64_default_or_url)
192 ? tables::base64::base64_default_or_url::d3
193 : ((options & base64_url) ? tables::base64::base64_url::d3
194 : tables::base64::base64_default::d3);
195 const bool ignore_garbage =
196 (options == base64_options::base64_url_accept_garbage) ||
197 (options == base64_options::base64_default_accept_garbage) ||
198 (options == base64_options::base64_default_or_url_accept_garbage);
200 const char_type *srcend = src + length;
201 const char_type *srcinit = src;
202 const char *dstinit = dst;
208 while (srcend - src >= 4 && is_eight_byte(src[0]) &&
209 is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
210 is_eight_byte(src[3]) &&
211 (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
212 d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
213 if (check_capacity && dstend - dst < 3) {
214 return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
215 size_t(dst - dstinit)};
217 *dst++ =
static_cast<char>(x & 0xFF);
218 *dst++ =
static_cast<char>((x >> 8) & 0xFF);
219 *dst++ =
static_cast<char>((x >> 16) & 0xFF);
222 const char_type *srccur = src;
227 if (ignore_garbage && src + 4 <= srcend) {
228 char_type c0 = src[0];
229 char_type c1 = src[1];
230 char_type c2 = src[2];
231 char_type c3 = src[3];
233 uint8_t code0 = to_base64[uint8_t(c0)];
234 uint8_t code1 = to_base64[uint8_t(c1)];
235 uint8_t code2 = to_base64[uint8_t(c2)];
236 uint8_t code3 = to_base64[uint8_t(c3)];
239 idx += (is_eight_byte(c0) && code0 <= 63);
241 idx += (is_eight_byte(c1) && code1 <= 63);
243 idx += (is_eight_byte(c2) && code2 <= 63);
245 idx += (is_eight_byte(c3) && code3 <= 63);
249 while ((idx < 4) && (src < srcend)) {
252 uint8_t code = to_base64[uint8_t(c)];
253 buffer[idx] = uint8_t(code);
254 if (is_eight_byte(c) && code <= 63) {
256 }
else if (!ignore_garbage &&
257 (code > 64 || !scalar::base64::is_eight_byte(c))) {
258 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
259 size_t(dst - dstinit)};
266 simdutf_log_assert(idx < 4,
"idx should be less than 4");
269 if (!ignore_garbage && (idx + padding_characters > 4)) {
270 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
271 size_t(dst - dstinit),
true};
278 if (!ignore_garbage &&
279 last_chunk_options == last_chunk_handling_options::loose &&
280 (idx >= 2) && padding_characters > 0 &&
281 ((idx + padding_characters) & 3) != 0) {
282 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
283 size_t(dst - dstinit),
true};
289 if (!ignore_garbage &&
290 last_chunk_options == last_chunk_handling_options::strict &&
291 (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
293 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
294 size_t(dst - dstinit),
true};
299 if ((last_chunk_options ==
300 last_chunk_handling_options::stop_before_partial &&
301 (padding_characters + idx < 4) && (idx != 0) &&
302 (idx >= 2 || padding_characters == 0)) ||
303 (last_chunk_options ==
304 last_chunk_handling_options::only_full_chunks &&
305 (idx >= 2 || padding_characters == 0))) {
309 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
312 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
313 (uint32_t(buffer[1]) << 2 * 6);
314 if (!ignore_garbage &&
315 (last_chunk_options == last_chunk_handling_options::strict) &&
317 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
318 size_t(dst - dstinit)};
320 if (check_capacity && dstend - dst < 1) {
321 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
322 size_t(dst - dstinit)};
324 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
325 }
else if (idx == 3) {
326 uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
327 (uint32_t(buffer[1]) << 2 * 6) +
328 (uint32_t(buffer[2]) << 1 * 6);
329 if (!ignore_garbage &&
330 (last_chunk_options == last_chunk_handling_options::strict) &&
332 return {BASE64_EXTRA_BITS, size_t(src - srcinit),
333 size_t(dst - dstinit)};
335 if (check_capacity && dstend - dst < 2) {
336 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
337 size_t(dst - dstinit)};
339 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
340 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
341 }
else if (!ignore_garbage && idx == 1 &&
342 (!is_partial(last_chunk_options) ||
343 (is_partial(last_chunk_options) &&
344 padding_characters > 0))) {
345 return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
346 size_t(dst - dstinit)};
347 }
else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
348 return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
349 size_t(dst - dstinit),
true};
351 return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
354 if (check_capacity && dstend - dst < 3) {
355 return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
356 size_t(dst - dstinit)};
359 (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
360 (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
361 *dst++ =
static_cast<char>((triple >> 16) & 0xFF);
362 *dst++ =
static_cast<char>((triple >> 8) & 0xFF);
363 *dst++ =
static_cast<char>(triple & 0xFF);
367template <
class char_type>
368simdutf_constexpr23 full_result base64_tail_decode(
369 char *dst,
const char_type *src,
size_t length,
370 size_t padding_characters,
372 base64_options options, last_chunk_handling_options last_chunk_options) {
373 return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
374 options, last_chunk_options);
381template <
class char_type>
382simdutf_constexpr23 full_result base64_tail_decode_safe(
383 char *dst,
size_t outlen,
const char_type *src,
size_t length,
384 size_t padding_characters,
386 base64_options options, last_chunk_handling_options last_chunk_options) {
387 return base64_tail_decode_impl<true>(dst, outlen, src, length,
388 padding_characters, options,
392inline simdutf_constexpr23 full_result
393patch_tail_result(full_result r,
size_t previous_input,
size_t previous_output,
394 size_t equallocation,
size_t full_input_length,
395 last_chunk_handling_options last_chunk_options) {
396 r.input_count += previous_input;
397 r.output_count += previous_output;
398 if (r.padding_error) {
399 r.input_count = equallocation;
402 if (r.error == error_code::SUCCESS) {
403 if (!is_partial(last_chunk_options)) {
406 r.input_count = full_input_length;
407 }
else if (r.output_count % 3 != 0) {
408 r.input_count = full_input_length;
416template <
bool use_lines = false>
417simdutf_constexpr23
size_t tail_encode_base64_impl(
418 char *dst,
const char *src,
size_t srclen, base64_options options,
419 size_t line_length = simdutf::default_line_length,
size_t line_offset = 0) {
420 if constexpr (use_lines) {
423 if (line_length < 4) {
426 simdutf_log_assert(line_offset <= line_length,
427 "line_offset should be less than line_length");
438 ((options & base64_url) == 0) ^
439 ((options & base64_reverse_padding) == base64_reverse_padding);
442 const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0
443 : tables::base64::base64_default::e0;
444 const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1
445 : tables::base64::base64_default::e1;
446 const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2
447 : tables::base64::base64_default::e2;
451 for (; i + 2 < srclen; i += 3) {
452 t1 = uint8_t(src[i]);
453 t2 = uint8_t(src[i + 1]);
454 t3 = uint8_t(src[i + 2]);
455 if constexpr (use_lines) {
456 if (line_offset + 3 >= line_length) {
457 if (line_offset == line_length) {
460 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
461 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
464 }
else if (line_offset + 1 == line_length) {
467 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
468 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
471 }
else if (line_offset + 2 == line_length) {
473 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
475 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
478 }
else if (line_offset + 3 == line_length) {
480 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
481 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
488 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
489 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
495 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
496 *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
500 switch (srclen - i) {
504 t1 = uint8_t(src[i]);
505 if constexpr (use_lines) {
507 if (line_offset + 3 >= line_length) {
508 if (line_offset == line_length) {
511 *out++ = e1[(t1 & 0x03) << 4];
514 }
else if (line_offset + 1 == line_length) {
517 *out++ = e1[(t1 & 0x03) << 4];
520 }
else if (line_offset + 2 == line_length) {
522 *out++ = e1[(t1 & 0x03) << 4];
526 }
else if (line_offset + 3 == line_length) {
528 *out++ = e1[(t1 & 0x03) << 4];
535 *out++ = e1[(t1 & 0x03) << 4];
540 if (line_offset + 2 >= line_length) {
541 if (line_offset == line_length) {
543 *out++ = e0[uint8_t(src[i])];
544 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
545 }
else if (line_offset + 1 == line_length) {
546 *out++ = e0[uint8_t(src[i])];
548 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
550 *out++ = e0[uint8_t(src[i])];
551 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
555 *out++ = e0[uint8_t(src[i])];
556 *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
561 *out++ = e1[(t1 & 0x03) << 4];
569 t1 = uint8_t(src[i]);
570 t2 = uint8_t(src[i + 1]);
571 if constexpr (use_lines) {
573 if (line_offset + 3 >= line_length) {
574 if (line_offset == line_length) {
577 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
578 *out++ = e2[(t2 & 0x0F) << 2];
580 }
else if (line_offset + 1 == line_length) {
583 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
584 *out++ = e2[(t2 & 0x0F) << 2];
586 }
else if (line_offset + 2 == line_length) {
588 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
590 *out++ = e2[(t2 & 0x0F) << 2];
592 }
else if (line_offset + 3 == line_length) {
594 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
595 *out++ = e2[(t2 & 0x0F) << 2];
601 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
602 *out++ = e2[(t2 & 0x0F) << 2];
606 if (line_offset + 3 >= line_length) {
607 if (line_offset == line_length) {
610 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
611 *out++ = e2[(t2 & 0x0F) << 2];
612 }
else if (line_offset + 1 == line_length) {
615 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
616 *out++ = e2[(t2 & 0x0F) << 2];
617 }
else if (line_offset + 2 == line_length) {
619 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
621 *out++ = e2[(t2 & 0x0F) << 2];
624 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
625 *out++ = e2[(t2 & 0x0F) << 2];
630 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
631 *out++ = e2[(t2 & 0x0F) << 2];
636 *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
637 *out++ = e2[(t2 & 0x0F) << 2];
643 return (
size_t)(out - dst);
648inline simdutf_constexpr23
size_t tail_encode_base64(
char *dst,
const char *src,
650 base64_options options) {
651 return tail_encode_base64_impl(dst, src, srclen, options);
654template <
class InputPtr>
655simdutf_warn_unused simdutf_constexpr23
size_t
656maximal_binary_length_from_base64(InputPtr input,
size_t length)
noexcept {
662 if (input[length - 1] ==
'=') {
664 if (length > 1 && input[length - 2] ==
'=') {
687 size_t actual_length = length - padding;
688 if (actual_length % 4 <= 1) {
689 return actual_length / 4 * 3;
693 return actual_length / 4 * 3 + (actual_length % 4) - 1;
700template <
class char_type>
701simdutf_warn_unused simdutf_constexpr23
size_t
702binary_length_from_base64(
const char_type *input,
size_t length)
noexcept {
705 for (
size_t i = 0; i < length; i++) {
706 count += (input[i] >
' ');
714 while (pos > 0 && padding < 2) {
715 char_type c = input[--pos];
718 }
else if (c >
' ') {
722 return ((count - padding) * 3) / 4;
725template <
typename char_type>
726simdutf_warn_unused simdutf_constexpr23 full_result
727base64_to_binary_details_impl(
728 const char_type *input,
size_t length,
char *output, base64_options options,
729 last_chunk_handling_options last_chunk_options)
noexcept {
730 const bool ignore_garbage =
731 (options == base64_options::base64_url_accept_garbage) ||
732 (options == base64_options::base64_default_accept_garbage) ||
733 (options == base64_options::base64_default_or_url_accept_garbage);
734 auto ri = simdutf::scalar::base64::find_end(input, length, options);
735 size_t equallocation = ri.equallocation;
736 size_t equalsigns = ri.equalsigns;
738 size_t full_input_length = ri.full_input_length;
740 if (!ignore_garbage && equalsigns > 0) {
741 return {INVALID_BASE64_CHARACTER, equallocation, 0,
true};
743 return {SUCCESS, full_input_length, 0};
745 full_result r = scalar::base64::base64_tail_decode(
746 output, input, length, equalsigns, options, last_chunk_options);
747 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
748 full_input_length, last_chunk_options);
749 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
750 equalsigns > 0 && !ignore_garbage) {
752 if ((r.output_count % 3 == 0) ||
753 ((r.output_count % 3) + 1 + equalsigns != 4)) {
754 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count,
true};
761 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
762 r.input_count < full_input_length) {
764 while (r.input_count < full_input_length &&
765 base64_ignorable(*(input + r.input_count), options)) {
770 if (r.input_count < full_input_length) {
771 while (r.input_count > 0 &&
772 base64_ignorable(*(input + r.input_count - 1), options)) {
780template <
typename char_type>
781simdutf_constexpr23 simdutf_warn_unused full_result
782base64_to_binary_details_safe_impl(
783 const char_type *input,
size_t length,
char *output,
size_t outlen,
784 base64_options options,
785 last_chunk_handling_options last_chunk_options)
noexcept {
786 const bool ignore_garbage =
787 (options == base64_options::base64_url_accept_garbage) ||
788 (options == base64_options::base64_default_accept_garbage) ||
789 (options == base64_options::base64_default_or_url_accept_garbage);
790 auto ri = simdutf::scalar::base64::find_end(input, length, options);
791 size_t equallocation = ri.equallocation;
792 size_t equalsigns = ri.equalsigns;
794 size_t full_input_length = ri.full_input_length;
796 if (!ignore_garbage && equalsigns > 0) {
797 return {INVALID_BASE64_CHARACTER, equallocation, 0};
799 return {SUCCESS, full_input_length, 0};
801 full_result r = scalar::base64::base64_tail_decode_safe(
802 output, outlen, input, length, equalsigns, options, last_chunk_options);
803 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
804 full_input_length, last_chunk_options);
805 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
806 equalsigns > 0 && !ignore_garbage) {
808 if ((r.output_count % 3 == 0) ||
809 ((r.output_count % 3) + 1 + equalsigns != 4)) {
810 return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
818 if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
819 r.input_count < full_input_length) {
821 while (r.input_count < full_input_length &&
822 base64_ignorable(*(input + r.input_count), options)) {
827 if (r.input_count < full_input_length) {
828 while (r.input_count > 0 &&
829 base64_ignorable(*(input + r.input_count - 1), options)) {
837simdutf_warn_unused simdutf_constexpr23
size_t
838base64_length_from_binary(
size_t length, base64_options options)
noexcept {
848 ((options & base64_url) == 0) ^
849 ((options & base64_reverse_padding) == base64_reverse_padding);
851 return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
853 return (length + 2) / 3 *
857simdutf_warn_unused simdutf_constexpr23
size_t
858base64_length_from_binary_with_lines(
size_t length, base64_options options,
859 size_t line_length)
noexcept {
863 size_t base64_length =
864 scalar::base64::base64_length_from_binary(length, options);
865 if (line_length < 4) {
869 (base64_length + line_length - 1) / line_length;
870 return base64_length + lines - 1;
878template <
typename char_type>
879simdutf_warn_unused
size_t prefix_length(
size_t count,
880 simdutf::base64_options options,
881 const char_type *input,
882 size_t length)
noexcept {
884 while (i < length && is_ignorable(input[i], options)) {
890 for (; i < length; i++) {
891 if (is_ignorable(input[i], options)) {
900 simdutf_log_assert(
false,
"You never get here");