simdutf 9.0.0
Unicode at GB/s.
Loading...
Searching...
No Matches
base64_implementation.h
1#ifndef SIMDUTF_BASE64_IMPLEMENTATION_H
2#define SIMDUTF_BASE64_IMPLEMENTATION_H
3
4// this is not part of the public api
5
6#include <type_traits> // for is_same
7
8namespace simdutf {
9
10template <typename chartype>
11simdutf_warn_unused simdutf_constexpr23 result slow_base64_to_binary_safe_impl(
12 const chartype *input, size_t length, char *output, size_t &outlen,
13 base64_options options,
14 last_chunk_handling_options last_chunk_options) noexcept {
15 const bool ignore_garbage = (options & base64_default_accept_garbage) != 0;
16 auto ri = simdutf::scalar::base64::find_end(input, length, options);
17 size_t equallocation = ri.equallocation;
18 size_t equalsigns = ri.equalsigns;
19 length = ri.srclen;
20 size_t full_input_length = ri.full_input_length;
21 (void)full_input_length;
22 if (length == 0) {
23 outlen = 0;
24 if (!ignore_garbage && equalsigns > 0) {
25 return {INVALID_BASE64_CHARACTER, equallocation};
26 }
27 return {SUCCESS, 0};
28 }
29
30 // The parameters of base64_tail_decode_safe are:
31 // - dst: the output buffer
32 // - outlen: the size of the output buffer
33 // - srcr: the input buffer
34 // - length: the size of the input buffer
35 // - padded_characters: the number of padding characters
36 // - options: the options for the base64 decoder
37 // - last_chunk_options: the options for the last chunk
38 // The function will return the number of bytes written to the output buffer
39 // and the number of bytes read from the input buffer.
40 // The function will also return an error code if the input buffer is not
41 // valid base64.
42 full_result r = scalar::base64::base64_tail_decode_safe(
43 output, outlen, input, length, equalsigns, options, last_chunk_options);
44 r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
45 full_input_length, last_chunk_options);
46 outlen = r.output_count;
47 if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
48 equalsigns > 0) {
49 // additional checks
50 if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) {
51 r.error = error_code::INVALID_BASE64_CHARACTER;
52 }
53 }
54 return {r.error, r.input_count}; // we cannot return r itself because it gets
55 // converted to error/output_count
56}
57
58template <typename chartype>
59simdutf_warn_unused simdutf_constexpr23 result base64_to_binary_safe_impl(
60 const chartype *input, size_t length, char *output, size_t &outlen,
61 base64_options options,
62 last_chunk_handling_options last_chunk_handling_options,
63 bool decode_up_to_bad_char) noexcept {
64 static_assert(std::is_same<chartype, char>::value ||
65 std::is_same<chartype, char16_t>::value,
66 "Only char and char16_t are supported.");
67 size_t remaining_input_length = length;
68 size_t remaining_output_length = outlen;
69 size_t input_position = 0;
70 size_t output_position = 0;
71
72 // We also do a first pass using the fast path to decode as much as possible
73 size_t safe_input = detail::min(
74 remaining_input_length,
75 base64_length_from_binary(remaining_output_length / 3 * 3, options));
76 bool done_with_partial = (safe_input == remaining_input_length);
78
79#if SIMDUTF_CPLUSPLUS23
80 if consteval {
81 r = scalar::base64::base64_to_binary_details_impl(
82 input + input_position, safe_input, output + output_position, options,
83 done_with_partial
84 ? last_chunk_handling_options
85 : simdutf::last_chunk_handling_options::only_full_chunks);
86 } else
87#endif
88 {
89 r = get_active_implementation()->base64_to_binary_details(
90 input + input_position, safe_input, output + output_position, options,
91 done_with_partial
92 ? last_chunk_handling_options
93 : simdutf::last_chunk_handling_options::only_full_chunks);
94 }
95 simdutf_log_assert(r.input_count <= safe_input,
96 "You should not read more than safe_input");
97 simdutf_log_assert(r.output_count <= remaining_output_length,
98 "You should not write more than remaining_output_length");
99 // Technically redundant, but we want to be explicit about it.
100 input_position += r.input_count;
101 output_position += r.output_count;
102 remaining_input_length -= r.input_count;
103 remaining_output_length -= r.output_count;
104 if (r.error != simdutf::error_code::SUCCESS) {
105 // There is an error. We return.
106 if (decode_up_to_bad_char &&
107 r.error == error_code::INVALID_BASE64_CHARACTER) {
108 return slow_base64_to_binary_safe_impl(
109 input, length, output, outlen, options, last_chunk_handling_options);
110 }
111 outlen = output_position;
112 return {r.error, input_position};
113 }
114
115 if (done_with_partial) {
116 // We are done. We have decoded everything.
117 outlen = output_position;
118 return {simdutf::error_code::SUCCESS, input_position};
119 }
120 // We have decoded some data, but we still have some data to decode.
121 // We need to decode the rest of the input buffer.
122 r = simdutf::scalar::base64::base64_to_binary_details_safe_impl(
123 input + input_position, remaining_input_length, output + output_position,
124 remaining_output_length, options, last_chunk_handling_options);
125 input_position += r.input_count;
126 output_position += r.output_count;
127 remaining_input_length -= r.input_count;
128 remaining_output_length -= r.output_count;
129
130 if (r.error != simdutf::error_code::SUCCESS) {
131 // There is an error. We return.
132 if (decode_up_to_bad_char &&
133 r.error == error_code::INVALID_BASE64_CHARACTER) {
134 return slow_base64_to_binary_safe_impl(
135 input, length, output, outlen, options, last_chunk_handling_options);
136 }
137 outlen = output_position;
138 return {r.error, input_position};
139 }
140 if (input_position < length) {
141 // We cannot process the entire input in one go, so we need to
142 // process it in two steps: first the fast path, then the slow path.
143 // In some cases, the processing might 'eat up' trailing ignorable
144 // characters in the fast path, but that can be a problem.
145 // suppose we have just white space followed by a single base64 character.
146 // If we first process the white space with the fast path, it will
147 // eat all of it. But, by the JavaScript standard, we should consume
148 // no character. See
149 // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
150 while (input_position > 0 &&
151 base64_ignorable(input[input_position - 1], options)) {
152 input_position--;
153 }
154 }
155 outlen = output_position;
156 return {simdutf::error_code::SUCCESS, input_position};
157}
158
159} // namespace simdutf
160#endif // SIMDUTF_BASE64_IMPLEMENTATION_H
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_DLLIMPORTEXPORT internal::atomic_ptr< const implementation > & get_active_implementation()
The active implementation.