/src/simdutf/src/haswell/avx2_base64.cpp
Line | Count | Source |
1 | | /** |
2 | | * References and further reading: |
3 | | * |
4 | | * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the |
5 | | * speed of a memory copy, Software: Practice and Experience 50 (2), 2020. |
6 | | * https://arxiv.org/abs/1910.05109 |
7 | | * |
8 | | * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2 |
9 | | * Instructions, ACM Transactions on the Web 12 (3), 2018. |
10 | | * https://arxiv.org/abs/1704.00605 |
11 | | * |
12 | | * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings. |
13 | | * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force, |
14 | | * Request for Comments: 4648. |
15 | | * |
16 | | * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization. |
17 | | * http://www.alfredklomp.com/programming/sse-base64/. (2014). |
18 | | * |
19 | | * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD |
20 | | * acceleration. https://github.com/aklomp/base64. (2014). |
21 | | * |
22 | | * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014). |
23 | | * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/ |
24 | | * |
25 | | * Nick Kopp. 2013. Base64 Encoding on a GPU. |
26 | | * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013). |
27 | | */ |
28 | | |
29 | | template <bool base64_url> |
30 | 0 | simdutf_really_inline __m256i lookup_pshufb_improved(const __m256i input) { |
31 | | // Precomputed shuffle masks for K = 1 to 16 |
32 | | // credit: Wojciech Muła |
33 | 0 | __m256i result = _mm256_subs_epu8(input, _mm256_set1_epi8(51)); |
34 | 0 | const __m256i less = _mm256_cmpgt_epi8(_mm256_set1_epi8(26), input); |
35 | 0 | result = |
36 | 0 | _mm256_or_si256(result, _mm256_and_si256(less, _mm256_set1_epi8(13))); |
37 | 0 | __m256i shift_LUT; |
38 | 0 | if (base64_url) { |
39 | 0 | shift_LUT = _mm256_setr_epi8( |
40 | 0 | 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, |
41 | 0 | '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0, |
42 | |
|
43 | 0 | 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, |
44 | 0 | '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0); |
45 | 0 | } else { |
46 | 0 | shift_LUT = _mm256_setr_epi8( |
47 | 0 | 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, |
48 | 0 | '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0, |
49 | |
|
50 | 0 | 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, |
51 | 0 | '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0); |
52 | 0 | } |
53 | |
|
54 | 0 | result = _mm256_shuffle_epi8(shift_LUT, result); |
55 | 0 | return _mm256_add_epi8(result, input); |
56 | 0 | } Unexecuted instantiation: simdutf.cpp:long long __vector(4) simdutf::haswell::(anonymous namespace)::lookup_pshufb_improved<true>(long long __vector(4)) Unexecuted instantiation: simdutf.cpp:long long __vector(4) simdutf::haswell::(anonymous namespace)::lookup_pshufb_improved<false>(long long __vector(4)) |
57 | | |
58 | 0 | simdutf_really_inline __m128i insert_line_feed16(__m128i input, int K) { |
59 | 0 | static const uint8_t shuffle_masks[16][16] = { |
60 | 0 | {15, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
61 | 0 | {0, 15, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
62 | 0 | {0, 1, 15, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
63 | 0 | {0, 1, 2, 15, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
64 | 0 | {0, 1, 2, 3, 15, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
65 | 0 | {0, 1, 2, 3, 4, 15, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
66 | 0 | {0, 1, 2, 3, 4, 5, 15, 6, 7, 8, 9, 10, 11, 12, 13, 14}, |
67 | 0 | {0, 1, 2, 3, 4, 5, 6, 15, 7, 8, 9, 10, 11, 12, 13, 14}, |
68 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 15, 8, 9, 10, 11, 12, 13, 14}, |
69 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 15, 9, 10, 11, 12, 13, 14}, |
70 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 10, 11, 12, 13, 14}, |
71 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 11, 12, 13, 14}, |
72 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15, 12, 13, 14}, |
73 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 15, 13, 14}, |
74 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 15, 14}, |
75 | 0 | {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}}; |
76 | 0 | input = _mm_insert_epi8(input, '\n', 15); |
77 | 0 | __m128i mask = _mm_loadu_si128((const __m128i *)shuffle_masks[K]); |
78 | 0 | return _mm_shuffle_epi8(input, mask); |
79 | 0 | } |
80 | | |
81 | 0 | simdutf_really_inline __m256i insert_line_feed32(__m256i input, int K) { |
82 | 0 | __m128i lo = _mm256_extracti128_si256(input, 0); |
83 | 0 | __m128i hi = _mm256_extracti128_si256(input, 1); |
84 | 0 | if (K >= 16) { |
85 | 0 | hi = insert_line_feed16(hi, K - 16); |
86 | 0 | } else { |
87 | 0 | hi = _mm_alignr_epi8(hi, lo, 15); |
88 | 0 | lo = insert_line_feed16(lo, K); |
89 | 0 | } |
90 | 0 | __m256i result = _mm256_castsi128_si256(lo); |
91 | 0 | result = _mm256_inserti128_si256(result, hi, 1); |
92 | 0 | return result; |
93 | 0 | } |
94 | | |
95 | | template <bool isbase64url, bool use_lines> |
96 | | size_t |
97 | | avx2_encode_base64_impl(char *dst, const char *src, size_t srclen, |
98 | | base64_options options, |
99 | 0 | size_t line_length = simdutf::default_line_length) { |
100 | 0 | size_t offset = 0; |
101 | |
|
102 | 0 | if (line_length < 4) { |
103 | 0 | line_length = 4; // We do not support line_length less than 4 |
104 | 0 | } |
105 | | // credit: Wojciech Muła |
106 | 0 | const uint8_t *input = (const uint8_t *)src; |
107 | |
|
108 | 0 | uint8_t *out = (uint8_t *)dst; |
109 | 0 | const __m256i shuf = |
110 | 0 | _mm256_set_epi8(10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1, |
111 | |
|
112 | 0 | 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1); |
113 | 0 | size_t i = 0; |
114 | 0 | for (; i + 100 <= srclen; i += 96) { |
115 | 0 | const __m128i lo0 = _mm_loadu_si128( |
116 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 0)); |
117 | 0 | const __m128i hi0 = _mm_loadu_si128( |
118 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 1)); |
119 | 0 | const __m128i lo1 = _mm_loadu_si128( |
120 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 2)); |
121 | 0 | const __m128i hi1 = _mm_loadu_si128( |
122 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 3)); |
123 | 0 | const __m128i lo2 = _mm_loadu_si128( |
124 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 4)); |
125 | 0 | const __m128i hi2 = _mm_loadu_si128( |
126 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 5)); |
127 | 0 | const __m128i lo3 = _mm_loadu_si128( |
128 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 6)); |
129 | 0 | const __m128i hi3 = _mm_loadu_si128( |
130 | 0 | reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 7)); |
131 | |
|
132 | 0 | __m256i in0 = _mm256_shuffle_epi8(_mm256_set_m128i(hi0, lo0), shuf); |
133 | 0 | __m256i in1 = _mm256_shuffle_epi8(_mm256_set_m128i(hi1, lo1), shuf); |
134 | 0 | __m256i in2 = _mm256_shuffle_epi8(_mm256_set_m128i(hi2, lo2), shuf); |
135 | 0 | __m256i in3 = _mm256_shuffle_epi8(_mm256_set_m128i(hi3, lo3), shuf); |
136 | |
|
137 | 0 | const __m256i t0_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x0fc0fc00)); |
138 | 0 | const __m256i t0_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x0fc0fc00)); |
139 | 0 | const __m256i t0_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x0fc0fc00)); |
140 | 0 | const __m256i t0_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x0fc0fc00)); |
141 | |
|
142 | 0 | const __m256i t1_0 = |
143 | 0 | _mm256_mulhi_epu16(t0_0, _mm256_set1_epi32(0x04000040)); |
144 | 0 | const __m256i t1_1 = |
145 | 0 | _mm256_mulhi_epu16(t0_1, _mm256_set1_epi32(0x04000040)); |
146 | 0 | const __m256i t1_2 = |
147 | 0 | _mm256_mulhi_epu16(t0_2, _mm256_set1_epi32(0x04000040)); |
148 | 0 | const __m256i t1_3 = |
149 | 0 | _mm256_mulhi_epu16(t0_3, _mm256_set1_epi32(0x04000040)); |
150 | |
|
151 | 0 | const __m256i t2_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x003f03f0)); |
152 | 0 | const __m256i t2_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x003f03f0)); |
153 | 0 | const __m256i t2_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x003f03f0)); |
154 | 0 | const __m256i t2_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x003f03f0)); |
155 | |
|
156 | 0 | const __m256i t3_0 = |
157 | 0 | _mm256_mullo_epi16(t2_0, _mm256_set1_epi32(0x01000010)); |
158 | 0 | const __m256i t3_1 = |
159 | 0 | _mm256_mullo_epi16(t2_1, _mm256_set1_epi32(0x01000010)); |
160 | 0 | const __m256i t3_2 = |
161 | 0 | _mm256_mullo_epi16(t2_2, _mm256_set1_epi32(0x01000010)); |
162 | 0 | const __m256i t3_3 = |
163 | 0 | _mm256_mullo_epi16(t2_3, _mm256_set1_epi32(0x01000010)); |
164 | |
|
165 | 0 | const __m256i input0 = _mm256_or_si256(t1_0, t3_0); |
166 | 0 | const __m256i input1 = _mm256_or_si256(t1_1, t3_1); |
167 | 0 | const __m256i input2 = _mm256_or_si256(t1_2, t3_2); |
168 | 0 | const __m256i input3 = _mm256_or_si256(t1_3, t3_3); |
169 | |
|
170 | 0 | if (use_lines) { |
171 | 0 | if (line_length >= 32) { // fast path |
172 | 0 | __m256i result; |
173 | 0 | result = lookup_pshufb_improved<isbase64url>(input0); |
174 | 0 | if (offset + 32 > line_length) { |
175 | 0 | size_t location_end = line_length - offset; |
176 | 0 | size_t to_move = 32 - location_end; |
177 | | // We could do this, or extract instead. |
178 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); |
179 | 0 | _mm256_storeu_si256( |
180 | 0 | reinterpret_cast<__m256i *>(out), |
181 | 0 | insert_line_feed32(result, static_cast<int>(location_end))); |
182 | 0 | offset = to_move; |
183 | 0 | out += 32 + 1; |
184 | 0 | } else { |
185 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); |
186 | 0 | offset += 32; |
187 | 0 | out += 32; |
188 | 0 | } |
189 | 0 | result = lookup_pshufb_improved<isbase64url>(input1); |
190 | |
|
191 | 0 | if (offset + 32 > line_length) { |
192 | 0 | size_t location_end = line_length - offset; |
193 | 0 | size_t to_move = 32 - location_end; |
194 | | |
195 | | // We could do this, or extract instead. |
196 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); |
197 | 0 | _mm256_storeu_si256( |
198 | 0 | reinterpret_cast<__m256i *>(out), |
199 | 0 | insert_line_feed32(result, static_cast<int>(location_end))); |
200 | | // see above. |
201 | | // out[32] = static_cast<uint8_t>(_mm256_extract_epi8(result, 31)); |
202 | 0 | offset = to_move; |
203 | 0 | out += 32 + 1; |
204 | 0 | } else { |
205 | |
|
206 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); |
207 | |
|
208 | 0 | offset += 32; |
209 | 0 | out += 32; |
210 | 0 | } |
211 | 0 | result = lookup_pshufb_improved<isbase64url>(input2); |
212 | |
|
213 | 0 | if (offset + 32 > line_length) { |
214 | 0 | size_t location_end = line_length - offset; |
215 | 0 | size_t to_move = 32 - location_end; |
216 | | |
217 | | // We could do this, or extract instead. |
218 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); |
219 | 0 | _mm256_storeu_si256( |
220 | 0 | reinterpret_cast<__m256i *>(out), |
221 | 0 | insert_line_feed32(result, static_cast<int>(location_end))); |
222 | | // see above. |
223 | | // out[32] = static_cast<uint8_t>(_mm256_extract_epi8(result, 31)); |
224 | 0 | offset = to_move; |
225 | 0 | out += 32 + 1; |
226 | 0 | } else { |
227 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); |
228 | 0 | offset += 32; |
229 | 0 | out += 32; |
230 | 0 | } |
231 | 0 | result = lookup_pshufb_improved<isbase64url>(input3); |
232 | |
|
233 | 0 | if (offset + 32 > line_length) { |
234 | 0 | size_t location_end = line_length - offset; |
235 | 0 | size_t to_move = 32 - location_end; |
236 | | |
237 | | // We could do this, or extract instead. |
238 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 1), result); |
239 | 0 | _mm256_storeu_si256( |
240 | 0 | reinterpret_cast<__m256i *>(out), |
241 | 0 | insert_line_feed32(result, static_cast<int>(location_end))); |
242 | | // see above. |
243 | | // out[32] = static_cast<uint8_t>(_mm256_extract_epi8(result, 31)); |
244 | 0 | offset = to_move; |
245 | 0 | out += 32 + 1; |
246 | 0 | } else { |
247 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), result); |
248 | 0 | offset += 32; |
249 | 0 | out += 32; |
250 | 0 | } |
251 | 0 | } else { // slow path |
252 | | // could be optimized |
253 | 0 | uint8_t buffer[128]; |
254 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer), |
255 | 0 | lookup_pshufb_improved<isbase64url>(input0)); |
256 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 32), |
257 | 0 | lookup_pshufb_improved<isbase64url>(input1)); |
258 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 64), |
259 | 0 | lookup_pshufb_improved<isbase64url>(input2)); |
260 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer + 96), |
261 | 0 | lookup_pshufb_improved<isbase64url>(input3)); |
262 | 0 | size_t out_pos = 0; |
263 | 0 | size_t local_offset = offset; |
264 | 0 | for (size_t j = 0; j < 128;) { |
265 | 0 | if (local_offset == line_length) { |
266 | 0 | out[out_pos++] = '\n'; |
267 | 0 | local_offset = 0; |
268 | 0 | } |
269 | 0 | out[out_pos++] = buffer[j++]; |
270 | 0 | local_offset++; |
271 | 0 | } |
272 | 0 | offset = local_offset; |
273 | 0 | out += out_pos; |
274 | 0 | } |
275 | 0 | } else { |
276 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), |
277 | 0 | lookup_pshufb_improved<isbase64url>(input0)); |
278 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 32), |
279 | 0 | lookup_pshufb_improved<isbase64url>(input1)); |
280 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 64), |
281 | 0 | lookup_pshufb_improved<isbase64url>(input2)); |
282 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out + 96), |
283 | 0 | lookup_pshufb_improved<isbase64url>(input3)); |
284 | |
|
285 | 0 | out += 128; |
286 | 0 | } |
287 | 0 | } |
288 | 0 | for (; i + 28 <= srclen; i += 24) { |
289 | | // lo = [xxxx|DDDC|CCBB|BAAA] |
290 | | // hi = [xxxx|HHHG|GGFF|FEEE] |
291 | 0 | const __m128i lo = |
292 | 0 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(input + i)); |
293 | 0 | const __m128i hi = |
294 | 0 | _mm_loadu_si128(reinterpret_cast<const __m128i *>(input + i + 4 * 3)); |
295 | | |
296 | | // bytes from groups A, B and C are needed in separate 32-bit lanes |
297 | | // in = [0HHH|0GGG|0FFF|0EEE[0DDD|0CCC|0BBB|0AAA] |
298 | 0 | __m256i in = _mm256_shuffle_epi8(_mm256_set_m128i(hi, lo), shuf); |
299 | | |
300 | | // this part is well commented in encode.sse.cpp |
301 | |
|
302 | 0 | const __m256i t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0fc0fc00)); |
303 | 0 | const __m256i t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040)); |
304 | 0 | const __m256i t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003f03f0)); |
305 | 0 | const __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010)); |
306 | 0 | const __m256i indices = _mm256_or_si256(t1, t3); |
307 | |
|
308 | 0 | if (use_lines) { |
309 | 0 | if (line_length >= 32) { // fast path |
310 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), |
311 | 0 | lookup_pshufb_improved<isbase64url>(indices)); |
312 | |
|
313 | 0 | if (offset + 32 > line_length) { |
314 | 0 | size_t location_end = line_length - offset; |
315 | 0 | size_t to_move = 32 - location_end; |
316 | 0 | std::memmove(out + location_end + 1, out + location_end, to_move); |
317 | 0 | out[location_end] = '\n'; |
318 | 0 | offset = to_move; |
319 | 0 | out += 32 + 1; |
320 | 0 | } else { |
321 | 0 | offset += 32; |
322 | 0 | out += 32; |
323 | 0 | } |
324 | 0 | } else { // slow path |
325 | | // could be optimized |
326 | 0 | alignas(32) uint8_t buffer[32]; |
327 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(buffer), |
328 | 0 | lookup_pshufb_improved<isbase64url>(indices)); |
329 | 0 | std::memcpy(out, buffer, 32); |
330 | 0 | size_t out_pos = 0; |
331 | 0 | size_t local_offset = offset; |
332 | 0 | for (size_t j = 0; j < 32;) { |
333 | 0 | if (local_offset == line_length) { |
334 | 0 | out[out_pos++] = '\n'; |
335 | 0 | local_offset = 0; |
336 | 0 | } |
337 | 0 | out[out_pos++] = buffer[j++]; |
338 | 0 | local_offset++; |
339 | 0 | } |
340 | 0 | offset = local_offset; |
341 | 0 | out += out_pos; |
342 | 0 | } |
343 | 0 | } else { |
344 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(out), |
345 | 0 | lookup_pshufb_improved<isbase64url>(indices)); |
346 | |
|
347 | 0 | out += 32; |
348 | 0 | } |
349 | 0 | } |
350 | 0 | return ((char *)out - (char *)dst) + |
351 | 0 | scalar::base64::tail_encode_base64_impl<use_lines>( |
352 | 0 | (char *)out, src + i, srclen - i, options, line_length, offset); |
353 | 0 | } Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::avx2_encode_base64_impl<true, false>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::avx2_encode_base64_impl<false, false>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::avx2_encode_base64_impl<true, true>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::avx2_encode_base64_impl<false, true>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long) |
354 | | |
355 | | template <bool isbase64url> |
356 | | size_t encode_base64(char *dst, const char *src, size_t srclen, |
357 | 0 | base64_options options) { |
358 | 0 | return avx2_encode_base64_impl<isbase64url, false>(dst, src, srclen, options); |
359 | 0 | } Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::encode_base64<true>(char*, char const*, unsigned long, simdutf::base64_options) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::encode_base64<false>(char*, char const*, unsigned long, simdutf::base64_options) |
360 | | |
361 | 0 | static inline void compress(__m128i data, uint16_t mask, char *output) { |
362 | 0 | if (mask == 0) { |
363 | 0 | _mm_storeu_si128(reinterpret_cast<__m128i *>(output), data); |
364 | 0 | return; |
365 | 0 | } |
366 | | // this particular implementation was inspired by work done by @animetosho |
367 | | // we do it in two steps, first 8 bytes and then second 8 bytes |
368 | 0 | uint8_t mask1 = uint8_t(mask); // least significant 8 bits |
369 | 0 | uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits |
370 | | // next line just loads the 64-bit values thintable_epi8[mask1] and |
371 | | // thintable_epi8[mask2] into a 128-bit register, using only |
372 | | // two instructions on most compilers. |
373 | |
|
374 | 0 | __m128i shufmask = _mm_set_epi64x(tables::base64::thintable_epi8[mask2], |
375 | 0 | tables::base64::thintable_epi8[mask1]); |
376 | | // we increment by 0x08 the second half of the mask |
377 | 0 | shufmask = |
378 | 0 | _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0)); |
379 | | // this is the version "nearly pruned" |
380 | 0 | __m128i pruned = _mm_shuffle_epi8(data, shufmask); |
381 | | // we still need to put the two halves together. |
382 | | // we compute the popcount of the first half: |
383 | 0 | int pop1 = tables::base64::BitsSetTable256mul2[mask1]; |
384 | | // then load the corresponding mask, what it does is to write |
385 | | // only the first pop1 bytes from the first 8 bytes, and then |
386 | | // it fills in with the bytes from the second 8 bytes + some filling |
387 | | // at the end. |
388 | 0 | __m128i compactmask = _mm_loadu_si128(reinterpret_cast<const __m128i *>( |
389 | 0 | tables::base64::pshufb_combine_table + pop1 * 8)); |
390 | 0 | __m128i answer = _mm_shuffle_epi8(pruned, compactmask); |
391 | |
|
392 | 0 | _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer); |
393 | 0 | } |
394 | | |
395 | | // --- decoding ----------------------------------------------- |
396 | | |
397 | | template <typename = void> |
398 | 0 | simdutf_really_inline void compress(__m256i data, uint32_t mask, char *output) { |
399 | 0 | if (mask == 0) { |
400 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), data); |
401 | 0 | return; |
402 | 0 | } |
403 | 0 | compress(_mm256_castsi256_si128(data), uint16_t(mask), output); |
404 | 0 | compress(_mm256_extracti128_si256(data, 1), uint16_t(mask >> 16), |
405 | 0 | output + count_ones(~mask & 0xFFFF)); |
406 | 0 | } |
407 | | |
408 | | template <typename = void> |
409 | 0 | simdutf_really_inline void base64_decode(char *out, __m256i str) { |
410 | | // credit: aqrit |
411 | 0 | const __m256i pack_shuffle = |
412 | 0 | _mm256_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1, |
413 | 0 | 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1); |
414 | 0 | const __m256i t0 = _mm256_maddubs_epi16(str, _mm256_set1_epi32(0x01400140)); |
415 | 0 | const __m256i t1 = _mm256_madd_epi16(t0, _mm256_set1_epi32(0x00011000)); |
416 | 0 | const __m256i t2 = _mm256_shuffle_epi8(t1, pack_shuffle); |
417 | | |
418 | | // Store the output: |
419 | 0 | _mm_storeu_si128((__m128i *)out, _mm256_castsi256_si128(t2)); |
420 | 0 | _mm_storeu_si128((__m128i *)(out + 12), _mm256_extracti128_si256(t2, 1)); |
421 | 0 | } |
422 | | |
423 | | template <typename = void> |
424 | 0 | simdutf_really_inline void base64_decode_block(char *out, const char *src) { |
425 | 0 | base64_decode(out, |
426 | 0 | _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src))); |
427 | 0 | base64_decode(out + 24, _mm256_loadu_si256( |
428 | 0 | reinterpret_cast<const __m256i *>(src + 32))); |
429 | 0 | } |
430 | | |
431 | | template <typename = void> |
432 | | simdutf_really_inline void base64_decode_block_safe(char *out, |
433 | 0 | const char *src) { |
434 | 0 | base64_decode(out, |
435 | 0 | _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src))); |
436 | 0 | alignas(32) char buffer[32]; // We enforce safety with a buffer. |
437 | 0 | base64_decode( |
438 | 0 | buffer, _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32))); |
439 | 0 | std::memcpy(out + 24, buffer, 24); |
440 | 0 | } |
441 | | |
442 | | // --- decoding - base64 class -------------------------------- |
443 | | |
444 | | class block64 { |
445 | | __m256i chunks[2]; |
446 | | |
447 | | public: |
448 | | // The caller of this function is responsible to ensure that there are 64 |
449 | | // bytes available from reading at src. |
450 | 0 | simdutf_really_inline block64(const char *src) { |
451 | 0 | chunks[0] = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src)); |
452 | 0 | chunks[1] = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32)); |
453 | 0 | } |
454 | | |
455 | | // The caller of this function is responsible to ensure that there are 128 |
456 | | // bytes available from reading at src. |
457 | 0 | simdutf_really_inline block64(const char16_t *src) { |
458 | 0 | const auto m1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src)); |
459 | 0 | const auto m2 = |
460 | 0 | _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 16)); |
461 | 0 | const auto m3 = |
462 | 0 | _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32)); |
463 | 0 | const auto m4 = |
464 | 0 | _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 48)); |
465 | |
|
466 | 0 | const auto m1p = _mm256_permute2x128_si256(m1, m2, 0x20); |
467 | 0 | const auto m2p = _mm256_permute2x128_si256(m1, m2, 0x31); |
468 | 0 | const auto m3p = _mm256_permute2x128_si256(m3, m4, 0x20); |
469 | 0 | const auto m4p = _mm256_permute2x128_si256(m3, m4, 0x31); |
470 | |
|
471 | 0 | chunks[0] = _mm256_packus_epi16(m1p, m2p); |
472 | 0 | chunks[1] = _mm256_packus_epi16(m3p, m4p); |
473 | 0 | } |
474 | | |
475 | 0 | simdutf_really_inline void copy_block(char *output) { |
476 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), chunks[0]); |
477 | 0 | _mm256_storeu_si256(reinterpret_cast<__m256i *>(output + 32), chunks[1]); |
478 | 0 | } |
479 | | |
480 | | // decode 64 bytes and output 48 bytes |
481 | 0 | simdutf_really_inline void base64_decode_block(char *out) { |
482 | 0 | base64_decode(out, chunks[0]); |
483 | 0 | base64_decode(out + 24, chunks[1]); |
484 | 0 | } |
485 | | |
486 | 0 | simdutf_really_inline void base64_decode_block_safe(char *out) { |
487 | 0 | base64_decode(out, chunks[0]); |
488 | 0 | alignas(32) char buffer[32]; // We enforce safety with a buffer. |
489 | 0 | base64_decode(buffer, chunks[1]); |
490 | 0 | std::memcpy(out + 24, buffer, 24); |
491 | 0 | } |
492 | | |
493 | | template <bool base64_url, bool ignore_garbage, bool default_or_url> |
494 | 0 | simdutf_really_inline uint64_t to_base64_mask(uint64_t *error) { |
495 | 0 | uint32_t err0 = 0; |
496 | 0 | uint32_t err1 = 0; |
497 | 0 | uint64_t m0 = to_base64_mask<base64_url, ignore_garbage, default_or_url>( |
498 | 0 | &chunks[0], &err0); |
499 | 0 | uint64_t m1 = to_base64_mask<base64_url, ignore_garbage, default_or_url>( |
500 | 0 | &chunks[1], &err1); |
501 | 0 | if (!ignore_garbage) { |
502 | 0 | *error = err0 | ((uint64_t)err1 << 32); |
503 | 0 | } |
504 | 0 | return m0 | (m1 << 32); |
505 | 0 | } Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, true, true>(unsigned long*) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, false, true>(unsigned long*) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<true, true, false>(unsigned long*) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<true, false, false>(unsigned long*) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, true, false>(unsigned long*) Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, false, false>(unsigned long*) |
506 | | |
507 | | template <bool base64_url, bool ignore_garbage, bool default_or_url> |
508 | 0 | simdutf_really_inline uint32_t to_base64_mask(__m256i *src, uint32_t *error) { |
509 | 0 | const __m256i ascii_space_tbl = |
510 | 0 | _mm256_setr_epi8(0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xa, |
511 | 0 | 0x0, 0xc, 0xd, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0, |
512 | 0 | 0x0, 0x0, 0x0, 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0); |
513 | | // credit: aqrit |
514 | 0 | __m256i delta_asso; |
515 | 0 | if (default_or_url) { |
516 | 0 | delta_asso = _mm256_setr_epi8( |
517 | 0 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, |
518 | 0 | 0x00, 0x00, 0x11, 0x00, 0x16, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
519 | 0 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x16); |
520 | 0 | } else if (base64_url) { |
521 | 0 | delta_asso = _mm256_setr_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, |
522 | 0 | 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF, 0x1, 0x1, |
523 | 0 | 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0, |
524 | 0 | 0x0, 0x0, 0xF, 0x0, 0xF); |
525 | 0 | } else { |
526 | 0 | delta_asso = _mm256_setr_epi8( |
527 | 0 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, |
528 | 0 | 0x00, 0x00, 0x0F, 0x00, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, |
529 | 0 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F); |
530 | 0 | } |
531 | |
|
532 | 0 | __m256i delta_values; |
533 | 0 | if (default_or_url) { |
534 | 0 | delta_values = _mm256_setr_epi8( |
535 | 0 | uint8_t(0xBF), uint8_t(0xE0), uint8_t(0xB9), uint8_t(0x13), |
536 | 0 | uint8_t(0x04), uint8_t(0xBF), uint8_t(0xBF), uint8_t(0xB9), |
537 | 0 | uint8_t(0xB9), uint8_t(0x00), uint8_t(0xFF), uint8_t(0x11), |
538 | 0 | uint8_t(0xFF), uint8_t(0xBF), uint8_t(0x10), uint8_t(0xB9), |
539 | 0 | uint8_t(0xBF), uint8_t(0xE0), uint8_t(0xB9), uint8_t(0x13), |
540 | 0 | uint8_t(0x04), uint8_t(0xBF), uint8_t(0xBF), uint8_t(0xB9), |
541 | 0 | uint8_t(0xB9), uint8_t(0x00), uint8_t(0xFF), uint8_t(0x11), |
542 | 0 | uint8_t(0xFF), uint8_t(0xBF), uint8_t(0x10), uint8_t(0xB9)); |
543 | 0 | } else if (base64_url) { |
544 | 0 | delta_values = _mm256_setr_epi8( |
545 | 0 | 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), uint8_t(0xBF), uint8_t(0xB9), |
546 | 0 | uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3), uint8_t(0xBF), uint8_t(0xE0), |
547 | 0 | uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), |
548 | 0 | uint8_t(0xBF), uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3), |
549 | 0 | uint8_t(0xBF), uint8_t(0xE0), uint8_t(0xB9), uint8_t(0xB9)); |
550 | 0 | } else { |
551 | 0 | delta_values = _mm256_setr_epi8( |
552 | 0 | int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), int8_t(0x04), |
553 | 0 | int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9), int8_t(0x00), |
554 | 0 | int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), |
555 | 0 | int8_t(0xB9), int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), |
556 | 0 | int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9), |
557 | 0 | int8_t(0x00), int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF), |
558 | 0 | int8_t(0xB9), int8_t(0xB9)); |
559 | 0 | } |
560 | |
|
561 | 0 | __m256i check_asso; |
562 | 0 | if (default_or_url) { |
563 | 0 | check_asso = _mm256_setr_epi8( |
564 | 0 | 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, |
565 | 0 | 0x07, 0x0B, 0x0E, 0x0B, 0x06, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, |
566 | 0 | 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0E, 0x0B, 0x06); |
567 | 0 | } else if (base64_url) { |
568 | 0 | check_asso = _mm256_setr_epi8(0xD, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, |
569 | 0 | 0x1, 0x3, 0x7, 0xB, 0xE, 0xB, 0x6, 0xD, 0x1, |
570 | 0 | 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3, |
571 | 0 | 0x7, 0xB, 0xE, 0xB, 0x6); |
572 | 0 | } else { |
573 | 0 | check_asso = _mm256_setr_epi8( |
574 | 0 | 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, |
575 | 0 | 0x07, 0x0B, 0x0B, 0x0B, 0x0F, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, |
576 | 0 | 0x01, 0x01, 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F); |
577 | 0 | } |
578 | 0 | __m256i check_values; |
579 | 0 | if (default_or_url) { |
580 | 0 | check_values = _mm256_setr_epi8( |
581 | 0 | uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), |
582 | 0 | uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xD5), uint8_t(0xA6), |
583 | 0 | uint8_t(0xB5), uint8_t(0xA1), uint8_t(0x00), uint8_t(0x80), |
584 | 0 | uint8_t(0x00), uint8_t(0x80), uint8_t(0x00), uint8_t(0x80), |
585 | 0 | uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), |
586 | 0 | uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xD5), uint8_t(0xA6), |
587 | 0 | uint8_t(0xB5), uint8_t(0xA1), uint8_t(0x00), uint8_t(0x80), |
588 | 0 | uint8_t(0x00), uint8_t(0x80), uint8_t(0x00), uint8_t(0x80)); |
589 | 0 | } else if (base64_url) { |
590 | 0 | check_values = _mm256_setr_epi8( |
591 | 0 | uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), |
592 | 0 | uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xB6), uint8_t(0xA6), |
593 | 0 | uint8_t(0xB5), uint8_t(0xA1), 0x0, uint8_t(0x80), 0x0, uint8_t(0x80), |
594 | 0 | 0x0, uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), |
595 | 0 | uint8_t(0x80), uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xB6), |
596 | 0 | uint8_t(0xA6), uint8_t(0xB5), uint8_t(0xA1), 0x0, uint8_t(0x80), 0x0, |
597 | 0 | uint8_t(0x80), 0x0, uint8_t(0x80)); |
598 | 0 | } else { |
599 | 0 | check_values = _mm256_setr_epi8( |
600 | 0 | int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0xCF), |
601 | 0 | int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5), int8_t(0x86), |
602 | 0 | int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80), int8_t(0x91), |
603 | 0 | int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), |
604 | 0 | int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5), |
605 | 0 | int8_t(0x86), int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80), |
606 | 0 | int8_t(0x91), int8_t(0x80)); |
607 | 0 | } |
608 | 0 | const __m256i shifted = _mm256_srli_epi32(*src, 3); |
609 | 0 | __m256i delta_hash = |
610 | 0 | _mm256_avg_epu8(_mm256_shuffle_epi8(delta_asso, *src), shifted); |
611 | 0 | if (default_or_url) { |
612 | 0 | delta_hash = _mm256_and_si256(delta_hash, _mm256_set1_epi8(0xf)); |
613 | 0 | } |
614 | 0 | const __m256i check_hash = |
615 | 0 | _mm256_avg_epu8(_mm256_shuffle_epi8(check_asso, *src), shifted); |
616 | 0 | const __m256i out = |
617 | 0 | _mm256_adds_epi8(_mm256_shuffle_epi8(delta_values, delta_hash), *src); |
618 | 0 | const __m256i chk = |
619 | 0 | _mm256_adds_epi8(_mm256_shuffle_epi8(check_values, check_hash), *src); |
620 | 0 | const int mask = _mm256_movemask_epi8(chk); |
621 | 0 | if (!ignore_garbage && mask) { |
622 | 0 | __m256i ascii_space = |
623 | 0 | _mm256_cmpeq_epi8(_mm256_shuffle_epi8(ascii_space_tbl, *src), *src); |
624 | 0 | *error = (mask ^ _mm256_movemask_epi8(ascii_space)); |
625 | 0 | } |
626 | 0 | *src = out; |
627 | 0 | return (uint32_t)mask; |
628 | 0 | } Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, true, true>(long long __vector(4)*, unsigned int*) Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, false, true>(long long __vector(4)*, unsigned int*) Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<true, true, false>(long long __vector(4)*, unsigned int*) Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<true, false, false>(long long __vector(4)*, unsigned int*) Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, true, false>(long long __vector(4)*, unsigned int*) Unexecuted instantiation: simdutf.cpp:unsigned int simdutf::haswell::(anonymous namespace)::block64::to_base64_mask<false, false, false>(long long __vector(4)*, unsigned int*) |
629 | | |
630 | 0 | simdutf_really_inline uint64_t compress_block(uint64_t mask, char *output) { |
631 | 0 | if (is_power_of_two(mask)) { |
632 | 0 | return compress_block_single(mask, output); |
633 | 0 | } |
634 | | |
635 | 0 | uint64_t nmask = ~mask; |
636 | 0 | compress(chunks[0], uint32_t(mask), output); |
637 | 0 | compress(chunks[1], uint32_t(mask >> 32), |
638 | 0 | output + count_ones(nmask & 0xFFFFFFFF)); |
639 | 0 | return count_ones(nmask); |
640 | 0 | } |
641 | | |
642 | | simdutf_really_inline size_t compress_block_single(uint64_t mask, |
643 | 0 | char *output) { |
644 | 0 | const size_t pos64 = trailing_zeroes(mask); |
645 | 0 | const int8_t pos = pos64 & 0xf; |
646 | 0 | switch (pos64 >> 4) { |
647 | 0 | case 0b00: { |
648 | 0 | const __m128i lane0 = _mm256_extracti128_si256(chunks[0], 0); |
649 | 0 | const __m128i lane1 = _mm256_extracti128_si256(chunks[0], 1); |
650 | |
|
651 | 0 | const __m128i v0 = _mm_set1_epi8(char(pos - 1)); |
652 | 0 | const __m128i v1 = |
653 | 0 | _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
654 | 0 | const __m128i v2 = _mm_cmpgt_epi8(v1, v0); |
655 | 0 | const __m128i sh = _mm_sub_epi8(v1, v2); |
656 | 0 | const __m128i compressed = _mm_shuffle_epi8(lane0, sh); |
657 | |
|
658 | 0 | _mm_storeu_si128((__m128i *)(output + 0 * 16), compressed); |
659 | 0 | _mm_storeu_si128((__m128i *)(output + 1 * 16 - 1), lane1); |
660 | 0 | _mm256_storeu_si256((__m256i *)(output + 2 * 16 - 1), chunks[1]); |
661 | 0 | } break; |
662 | 0 | case 0b01: { |
663 | 0 | const __m128i lane0 = _mm256_extracti128_si256(chunks[0], 0); |
664 | 0 | const __m128i lane1 = _mm256_extracti128_si256(chunks[0], 1); |
665 | 0 | _mm_storeu_si128((__m128i *)(output + 0 * 16), lane0); |
666 | |
|
667 | 0 | const __m128i v0 = _mm_set1_epi8(char(pos - 1)); |
668 | 0 | const __m128i v1 = |
669 | 0 | _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
670 | 0 | const __m128i v2 = _mm_cmpgt_epi8(v1, v0); |
671 | 0 | const __m128i sh = _mm_sub_epi8(v1, v2); |
672 | 0 | const __m128i compressed = _mm_shuffle_epi8(lane1, sh); |
673 | |
|
674 | 0 | _mm_storeu_si128((__m128i *)(output + 1 * 16), compressed); |
675 | 0 | _mm256_storeu_si256((__m256i *)(output + 2 * 16 - 1), chunks[1]); |
676 | 0 | } break; |
677 | 0 | case 0b10: { |
678 | 0 | const __m128i lane2 = _mm256_extracti128_si256(chunks[1], 0); |
679 | 0 | const __m128i lane3 = _mm256_extracti128_si256(chunks[1], 1); |
680 | |
|
681 | 0 | _mm256_storeu_si256((__m256i *)(output + 0 * 16), chunks[0]); |
682 | |
|
683 | 0 | const __m128i v0 = _mm_set1_epi8(char(pos - 1)); |
684 | 0 | const __m128i v1 = |
685 | 0 | _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
686 | 0 | const __m128i v2 = _mm_cmpgt_epi8(v1, v0); |
687 | 0 | const __m128i sh = _mm_sub_epi8(v1, v2); |
688 | 0 | const __m128i compressed = _mm_shuffle_epi8(lane2, sh); |
689 | |
|
690 | 0 | _mm_storeu_si128((__m128i *)(output + 2 * 16), compressed); |
691 | 0 | _mm_storeu_si128((__m128i *)(output + 3 * 16 - 1), lane3); |
692 | 0 | } break; |
693 | 0 | case 0b11: { |
694 | 0 | const __m128i lane2 = _mm256_extracti128_si256(chunks[1], 0); |
695 | 0 | const __m128i lane3 = _mm256_extracti128_si256(chunks[1], 1); |
696 | |
|
697 | 0 | _mm256_storeu_si256((__m256i *)(output + 0 * 16), chunks[0]); |
698 | 0 | _mm_storeu_si128((__m128i *)(output + 2 * 16), lane2); |
699 | |
|
700 | 0 | const __m128i v0 = _mm_set1_epi8(char(pos - 1)); |
701 | 0 | const __m128i v1 = |
702 | 0 | _mm_setr_epi8(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); |
703 | 0 | const __m128i v2 = _mm_cmpgt_epi8(v1, v0); |
704 | 0 | const __m128i sh = _mm_sub_epi8(v1, v2); |
705 | 0 | const __m128i compressed = _mm_shuffle_epi8(lane3, sh); |
706 | |
|
707 | 0 | _mm_storeu_si128((__m128i *)(output + 3 * 16), compressed); |
708 | 0 | } break; |
709 | 0 | } |
710 | | |
711 | 0 | return 63; |
712 | 0 | } |
713 | | }; |
714 | | |
715 | | simdutf_warn_unused size_t avx2_binary_length_from_base64(const char *input, |
716 | 0 | size_t length) { |
717 | 0 | size_t count = 0; |
718 | 0 | const char *ptr = input; |
719 | 0 | const char *end = input + length; |
720 | |
|
721 | 0 | __m256i spaces = _mm256_set1_epi8(0x20); |
722 | 0 | while (ptr + 32 <= end) { |
723 | 0 | __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr)); |
724 | 0 | __m256i gt_space = _mm256_cmpgt_epi8(data, spaces); |
725 | 0 | uint32_t mask = static_cast<uint32_t>(_mm256_movemask_epi8(gt_space)); |
726 | 0 | count += count_ones(mask); |
727 | 0 | ptr += 32; |
728 | 0 | } |
729 | |
|
730 | 0 | while (ptr < end) { |
731 | 0 | count += (*ptr > 0x20) ? 1 : 0; |
732 | 0 | ptr++; |
733 | 0 | } |
734 | |
|
735 | 0 | size_t padding = 0; |
736 | 0 | size_t pos = length; |
737 | 0 | while (pos > 0 && padding < 2) { |
738 | 0 | char c = input[--pos]; |
739 | 0 | if (c == '=') { |
740 | 0 | padding++; |
741 | 0 | } else if (c > ' ') { |
742 | 0 | break; |
743 | 0 | } |
744 | 0 | } |
745 | 0 | return ((count - padding) * 3) / 4; |
746 | 0 | } |
747 | | |
748 | | simdutf_warn_unused size_t avx2_binary_length_from_base64(const char16_t *input, |
749 | 0 | size_t length) { |
750 | 0 | size_t count = 0; |
751 | 0 | const char16_t *ptr = input; |
752 | 0 | const char16_t *end = input + length; |
753 | |
|
754 | 0 | __m256i spaces = _mm256_set1_epi16(0x20); |
755 | 0 | while (ptr + 16 <= end) { |
756 | 0 | __m256i data = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(ptr)); |
757 | 0 | __m256i gt_space = _mm256_cmpgt_epi16(data, spaces); |
758 | 0 | uint32_t mask = static_cast<uint32_t>(_mm256_movemask_epi8(gt_space)); |
759 | 0 | count += count_ones(mask); |
760 | 0 | ptr += 16; |
761 | 0 | } |
762 | 0 | count /= 2; |
763 | |
|
764 | 0 | while (ptr < end) { |
765 | 0 | count += (*ptr > 0x20) ? 1 : 0; |
766 | 0 | ptr++; |
767 | 0 | } |
768 | |
|
769 | 0 | size_t padding = 0; |
770 | 0 | size_t pos = length; |
771 | 0 | while (pos > 0 && padding < 2) { |
772 | 0 | char16_t c = input[--pos]; |
773 | 0 | if (c == '=') { |
774 | 0 | padding++; |
775 | 0 | } else if (c > ' ') { |
776 | 0 | break; |
777 | 0 | } |
778 | 0 | } |
779 | 0 | return ((count - padding) * 3) / 4; |
780 | 0 | } |