Coverage Report

Created: 2026-07-16 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}