Coverage Report

Created: 2026-07-25 06:54

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