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