/src/simdutf/include/simdutf/base64_implementation.h
Line | Count | Source |
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 <algorithm> // for std::min |
7 | | |
8 | | namespace simdutf { |
9 | | |
10 | | template <typename chartype> |
11 | | simdutf_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 | 0 | last_chunk_handling_options last_chunk_options) noexcept { |
15 | 0 | const bool ignore_garbage = (options & base64_default_accept_garbage) != 0; |
16 | 0 | auto ri = simdutf::scalar::base64::find_end(input, length, options); |
17 | 0 | size_t equallocation = ri.equallocation; |
18 | 0 | size_t equalsigns = ri.equalsigns; |
19 | 0 | length = ri.srclen; |
20 | 0 | size_t full_input_length = ri.full_input_length; |
21 | 0 | (void)full_input_length; |
22 | 0 | if (length == 0) { |
23 | 0 | outlen = 0; |
24 | 0 | if (!ignore_garbage && equalsigns > 0) { |
25 | 0 | return {INVALID_BASE64_CHARACTER, equallocation}; |
26 | 0 | } |
27 | 0 | return {SUCCESS, 0}; |
28 | 0 | } |
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 | 0 | full_result r = scalar::base64::base64_tail_decode_safe( |
43 | 0 | output, outlen, input, length, equalsigns, options, last_chunk_options); |
44 | 0 | r = scalar::base64::patch_tail_result(r, 0, 0, equallocation, |
45 | 0 | full_input_length, last_chunk_options); |
46 | 0 | outlen = r.output_count; |
47 | 0 | if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS && |
48 | 0 | equalsigns > 0) { |
49 | | // additional checks |
50 | 0 | if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) { |
51 | 0 | r.error = error_code::INVALID_BASE64_CHARACTER; |
52 | 0 | } |
53 | 0 | } |
54 | 0 | return {r.error, r.input_count}; // we cannot return r itself because it gets |
55 | | // converted to error/output_count |
56 | 0 | } Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options) Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options) |
57 | | |
58 | | template <typename chartype> |
59 | | simdutf_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 | 0 | bool decode_up_to_bad_char) noexcept { |
64 | 0 | static_assert(std::is_same<chartype, char>::value || |
65 | 0 | std::is_same<chartype, char16_t>::value, |
66 | 0 | "Only char and char16_t are supported."); |
67 | 0 | size_t remaining_input_length = length; |
68 | 0 | size_t remaining_output_length = outlen; |
69 | 0 | size_t input_position = 0; |
70 | 0 | size_t output_position = 0; |
71 | | |
72 | | // We also do a first pass using the fast path to decode as much as possible |
73 | 0 | size_t safe_input = (std::min)( |
74 | 0 | remaining_input_length, |
75 | 0 | base64_length_from_binary(remaining_output_length / 3 * 3, options)); |
76 | 0 | bool done_with_partial = (safe_input == remaining_input_length); |
77 | 0 | simdutf::full_result r; |
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 | 0 | { |
89 | 0 | r = get_active_implementation()->base64_to_binary_details( |
90 | 0 | input + input_position, safe_input, output + output_position, options, |
91 | 0 | done_with_partial |
92 | 0 | ? last_chunk_handling_options |
93 | 0 | : simdutf::last_chunk_handling_options::only_full_chunks); |
94 | 0 | } |
95 | 0 | simdutf_log_assert(r.input_count <= safe_input, |
96 | 0 | "You should not read more than safe_input"); |
97 | 0 | simdutf_log_assert(r.output_count <= remaining_output_length, |
98 | 0 | "You should not write more than remaining_output_length"); |
99 | | // Technically redundant, but we want to be explicit about it. |
100 | 0 | input_position += r.input_count; |
101 | 0 | output_position += r.output_count; |
102 | 0 | remaining_input_length -= r.input_count; |
103 | 0 | remaining_output_length -= r.output_count; |
104 | 0 | if (r.error != simdutf::error_code::SUCCESS) { |
105 | | // There is an error. We return. |
106 | 0 | if (decode_up_to_bad_char && |
107 | 0 | r.error == error_code::INVALID_BASE64_CHARACTER) { |
108 | 0 | return slow_base64_to_binary_safe_impl( |
109 | 0 | input, length, output, outlen, options, last_chunk_handling_options); |
110 | 0 | } |
111 | 0 | outlen = output_position; |
112 | 0 | return {r.error, input_position}; |
113 | 0 | } |
114 | | |
115 | 0 | if (done_with_partial) { |
116 | | // We are done. We have decoded everything. |
117 | 0 | outlen = output_position; |
118 | 0 | return {simdutf::error_code::SUCCESS, input_position}; |
119 | 0 | } |
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 | 0 | r = simdutf::scalar::base64::base64_to_binary_details_safe_impl( |
123 | 0 | input + input_position, remaining_input_length, output + output_position, |
124 | 0 | remaining_output_length, options, last_chunk_handling_options); |
125 | 0 | input_position += r.input_count; |
126 | 0 | output_position += r.output_count; |
127 | 0 | remaining_input_length -= r.input_count; |
128 | 0 | remaining_output_length -= r.output_count; |
129 | |
|
130 | 0 | if (r.error != simdutf::error_code::SUCCESS) { |
131 | | // There is an error. We return. |
132 | 0 | if (decode_up_to_bad_char && |
133 | 0 | r.error == error_code::INVALID_BASE64_CHARACTER) { |
134 | 0 | return slow_base64_to_binary_safe_impl( |
135 | 0 | input, length, output, outlen, options, last_chunk_handling_options); |
136 | 0 | } |
137 | 0 | outlen = output_position; |
138 | 0 | return {r.error, input_position}; |
139 | 0 | } |
140 | 0 | 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 | 0 | while (input_position > 0 && |
151 | 0 | base64_ignorable(input[input_position - 1], options)) { |
152 | 0 | input_position--; |
153 | 0 | } |
154 | 0 | } |
155 | 0 | outlen = output_position; |
156 | 0 | return {simdutf::error_code::SUCCESS, input_position}; |
157 | 0 | } Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool) Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool) |
158 | | |
159 | | } // namespace simdutf |
160 | | #endif // SIMDUTF_BASE64_IMPLEMENTATION_H |