Coverage Report

Created: 2026-07-25 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/simdutf/src/icelake/implementation.cpp
Line
Count
Source
1
#include <tuple>
2
#include <utility>
3
#include "simdutf/icelake/intrinsics.h"
4
5
#include "simdutf/icelake/begin.h"
6
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7
  // transcoding from UTF-16 to UTF-8 (self-wrapping generic header; must be
8
  // included at namespace scope zero, unlike icelake's own .inl.cpp files which
9
  // are included inside the simdutf::icelake namespace below)
10
  #include "generic/utf16_to_utf8/utf16_to_utf8_with_replacement.h"
11
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
12
namespace simdutf {
13
namespace SIMDUTF_IMPLEMENTATION {
14
namespace {
15
#ifndef SIMDUTF_ICELAKE_H
16
  #error "icelake.h must be included"
17
#endif
18
using namespace simd;
19
20
#include "icelake/icelake_macros.inl.cpp"
21
#include "icelake/icelake_common.inl.cpp"
22
#if SIMDUTF_FEATURE_UTF8
23
  #include "icelake/icelake_utf8_common.inl.cpp"
24
#endif // SIMDUTF_FEATURE_UTF8
25
26
#if SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
27
  #include "icelake/icelake_utf8_validation.inl.cpp"
28
#endif // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
29
30
#if SIMDUTF_FEATURE_UTF8 &&                                                    \
31
    (SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_LATIN1)
32
  #include "icelake/icelake_from_valid_utf8.inl.cpp"
33
  #include "icelake/icelake_from_utf8.inl.cpp"
34
#endif // SIMDUTF_FEATURE_UTF8 && (SIMDUTF_FEATURE_UTF16 ||
35
       // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_LATIN1)
36
37
#if SIMDUTF_FEATURE_UTF16
38
  #include "icelake/icelake_utf16fix.cpp"
39
#endif // SIMDUTF_FEATURE_UTF16
40
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
41
  #include "icelake/icelake_convert_utf8_to_latin1.inl.cpp"
42
  #include "icelake/icelake_convert_valid_utf8_to_latin1.inl.cpp"
43
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
44
45
#if SIMDUTF_FEATURE_UTF16
46
  #include "icelake/icelake_convert_utf16_to_latin1.inl.cpp"
47
#endif // SIMDUTF_FEATURE_UTF16
48
49
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
50
  #include "icelake/icelake_convert_utf16_to_utf8.inl.cpp"
51
  #include "icelake/icelake_convert_utf8_to_utf16.inl.cpp"
52
  #include "icelake/icelake_utf8_length_from_utf16.inl.cpp"
53
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
54
55
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
56
  #include "icelake/icelake_convert_utf16_to_utf32.inl.cpp"
57
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
58
59
#if SIMDUTF_FEATURE_UTF32
60
  #include "icelake/icelake_convert_utf32_to_latin1.inl.cpp"
61
#endif // SIMDUTF_FEATURE_UTF32
62
63
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
64
  #include "icelake/icelake_convert_utf32_to_utf8.inl.cpp"
65
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
66
67
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
68
  #include "icelake/icelake_convert_utf32_to_utf16.inl.cpp"
69
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
70
71
#if SIMDUTF_FEATURE_ASCII
72
  #include "icelake/icelake_ascii_validation.inl.cpp"
73
#endif // SIMDUTF_FEATURE_ASCII
74
#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
75
  #include "icelake/icelake_utf32_validation.inl.cpp"
76
#endif // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
77
#if SIMDUTF_FEATURE_UTF8
78
  #include "icelake/icelake_convert_latin1_to_utf8.inl.cpp"
79
#endif // SIMDUTF_FEATURE_UTF8
80
#if SIMDUTF_FEATURE_UTF16
81
  #include "icelake/icelake_convert_latin1_to_utf16.inl.cpp"
82
#endif // SIMDUTF_FEATURE_UTF16
83
#if SIMDUTF_FEATURE_UTF32
84
  #include "icelake/icelake_convert_latin1_to_utf32.inl.cpp"
85
#endif // SIMDUTF_FEATURE_UTF32
86
#if SIMDUTF_FEATURE_BASE64
87
  #include "icelake/icelake_base64.inl.cpp"
88
  #include "icelake/icelake_find.inl.cpp"
89
#endif // SIMDUTF_FEATURE_BASE64
90
91
#include <cstdint>
92
93
} // namespace
94
} // namespace SIMDUTF_IMPLEMENTATION
95
} // namespace simdutf
96
97
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
98
  #include "generic/utf32.h"
99
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
100
101
namespace simdutf {
102
namespace SIMDUTF_IMPLEMENTATION {
103
104
#if SIMDUTF_FEATURE_DETECT_ENCODING
105
simdutf_warn_unused int
106
implementation::detect_encodings(const char *input,
107
0
                                 size_t length) const noexcept {
108
  // If there is a BOM, then we trust it.
109
0
  auto bom_encoding = simdutf::BOM::check_bom(input, length);
110
0
  if (bom_encoding != encoding_type::unspecified) {
111
0
    return bom_encoding;
112
0
  }
113
114
0
  int out = 0;
115
0
  uint32_t utf16_err = (length % 2);
116
0
  uint32_t utf32_err = (length % 4);
117
0
  uint32_t ends_with_high = 0;
118
0
  avx512_utf8_checker checker{};
119
0
  const __m512i offset = _mm512_set1_epi32((uint32_t)0xffff2000);
120
0
  __m512i currentmax = _mm512_setzero_si512();
121
0
  __m512i currentoffsetmax = _mm512_setzero_si512();
122
0
  const char *ptr = input;
123
0
  const char *end = ptr + length;
124
0
  for (; end - ptr >= 64; ptr += 64) {
125
    // utf8 checks
126
0
    const __m512i data = _mm512_loadu_si512((const __m512i *)ptr);
127
0
    checker.check_next_input(data);
128
129
    // utf16le_checks
130
0
    __m512i diff = _mm512_sub_epi16(data, _mm512_set1_epi16(uint16_t(0xD800)));
131
0
    __mmask32 surrogates =
132
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
133
0
    __mmask32 highsurrogates =
134
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
135
0
    __mmask32 lowsurrogates = surrogates ^ highsurrogates;
136
0
    utf16_err |= (((highsurrogates << 1) | ends_with_high) != lowsurrogates);
137
0
    ends_with_high = ((highsurrogates & 0x80000000) != 0);
138
139
    // utf32le checks
140
0
    currentoffsetmax =
141
0
        _mm512_max_epu32(_mm512_add_epi32(data, offset), currentoffsetmax);
142
0
    currentmax = _mm512_max_epu32(data, currentmax);
143
0
  }
144
145
  // last block with 0 <= len < 64
146
0
  __mmask64 read_mask = (__mmask64(1) << (end - ptr)) - 1;
147
0
  const __m512i data = _mm512_maskz_loadu_epi8(read_mask, (const __m512i *)ptr);
148
0
  checker.check_next_input(data);
149
150
0
  __m512i diff = _mm512_sub_epi16(data, _mm512_set1_epi16(uint16_t(0xD800)));
151
0
  __mmask32 surrogates =
152
0
      _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
153
0
  __mmask32 highsurrogates =
154
0
      _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
155
0
  __mmask32 lowsurrogates = surrogates ^ highsurrogates;
156
0
  utf16_err |= (((highsurrogates << 1) | ends_with_high) != lowsurrogates);
157
158
0
  currentoffsetmax =
159
0
      _mm512_max_epu32(_mm512_add_epi32(data, offset), currentoffsetmax);
160
0
  currentmax = _mm512_max_epu32(data, currentmax);
161
162
0
  const __m512i standardmax = _mm512_set1_epi32((uint32_t)0x10ffff);
163
0
  const __m512i standardoffsetmax = _mm512_set1_epi32((uint32_t)0xfffff7ff);
164
0
  __m512i is_zero =
165
0
      _mm512_xor_si512(_mm512_max_epu32(currentmax, standardmax), standardmax);
166
0
  utf32_err |= (_mm512_test_epi8_mask(is_zero, is_zero) != 0);
167
0
  is_zero = _mm512_xor_si512(
168
0
      _mm512_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
169
0
  utf32_err |= (_mm512_test_epi8_mask(is_zero, is_zero) != 0);
170
0
  checker.check_eof();
171
0
  bool is_valid_utf8 = !checker.errors();
172
0
  if (is_valid_utf8) {
173
0
    out |= encoding_type::UTF8;
174
0
  }
175
0
  if (utf16_err == 0) {
176
0
    out |= encoding_type::UTF16_LE;
177
0
  }
178
0
  if (utf32_err == 0) {
179
0
    out |= encoding_type::UTF32_LE;
180
0
  }
181
0
  return out;
182
0
}
183
#endif // SIMDUTF_FEATURE_DETECT_ENCODING
184
185
#if SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
186
simdutf_warn_unused bool
187
0
implementation::validate_utf8(const char *buf, size_t len) const noexcept {
188
0
  if (simdutf_unlikely(len == 0)) {
189
0
    return true;
190
0
  }
191
0
  avx512_utf8_checker checker{};
192
0
  const char *ptr = buf;
193
0
  const char *end = ptr + len;
194
0
  for (; end - ptr >= 64; ptr += 64) {
195
0
    const __m512i utf8 = _mm512_loadu_si512((const __m512i *)ptr);
196
0
    checker.check_next_input(utf8);
197
0
  }
198
0
  if (end != ptr) {
199
0
    const __m512i utf8 = _mm512_maskz_loadu_epi8(
200
0
        ~UINT64_C(0) >> (64 - (end - ptr)), (const __m512i *)ptr);
201
0
    checker.check_next_input(utf8);
202
0
  }
203
0
  checker.check_eof();
204
0
  return !checker.errors();
205
0
}
206
#endif // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
207
208
#if SIMDUTF_FEATURE_UTF8
209
simdutf_warn_unused result implementation::validate_utf8_with_errors(
210
0
    const char *buf, size_t len) const noexcept {
211
0
  if (simdutf_unlikely(len == 0)) {
212
0
    return result(error_code::SUCCESS, len);
213
0
  }
214
0
  avx512_utf8_checker checker{};
215
0
  const char *ptr = buf;
216
0
  const char *end = ptr + len;
217
0
  size_t count{0};
218
0
  for (; end - ptr >= 64; ptr += 64) {
219
0
    const __m512i utf8 = _mm512_loadu_si512((const __m512i *)ptr);
220
0
    checker.check_next_input(utf8);
221
0
    if (checker.errors()) {
222
0
      if (count != 0) {
223
0
        count--;
224
0
      } // Sometimes the error is only detected in the next chunk
225
0
      result res = scalar::utf8::rewind_and_validate_with_errors(
226
0
          reinterpret_cast<const char *>(buf),
227
0
          reinterpret_cast<const char *>(buf + count), len - count);
228
0
      res.count += count;
229
0
      return res;
230
0
    }
231
0
    count += 64;
232
0
  }
233
0
  if (end != ptr) {
234
0
    const __m512i utf8 = _mm512_maskz_loadu_epi8(
235
0
        ~UINT64_C(0) >> (64 - (end - ptr)), (const __m512i *)ptr);
236
0
    checker.check_next_input(utf8);
237
0
  }
238
0
  checker.check_eof();
239
0
  if (checker.errors()) {
240
0
    if (count != 0) {
241
0
      count--;
242
0
    } // Sometimes the error is only detected in the next chunk
243
0
    result res = scalar::utf8::rewind_and_validate_with_errors(
244
0
        reinterpret_cast<const char *>(buf),
245
0
        reinterpret_cast<const char *>(buf + count), len - count);
246
0
    res.count += count;
247
0
    return res;
248
0
  }
249
0
  return result(error_code::SUCCESS, len);
250
0
}
251
#endif // SIMDUTF_FEATURE_UTF8
252
253
#if SIMDUTF_FEATURE_ASCII
254
simdutf_warn_unused bool
255
0
implementation::validate_ascii(const char *buf, size_t len) const noexcept {
256
0
  return icelake::validate_ascii(buf, len);
257
0
}
258
259
simdutf_warn_unused result implementation::validate_ascii_with_errors(
260
0
    const char *buf, size_t len) const noexcept {
261
0
  const char *buf_orig = buf;
262
0
  const char *end = buf + len;
263
0
  const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80);
264
0
  for (; end - buf >= 64; buf += 64) {
265
0
    const __m512i input = _mm512_loadu_si512((const __m512i *)buf);
266
0
    __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
267
0
    if (notascii) {
268
0
      return result(error_code::TOO_LARGE,
269
0
                    buf - buf_orig + _tzcnt_u64(notascii));
270
0
    }
271
0
  }
272
0
  if (end != buf) {
273
0
    const __m512i input = _mm512_maskz_loadu_epi8(
274
0
        ~UINT64_C(0) >> (64 - (end - buf)), (const __m512i *)buf);
275
0
    __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
276
0
    if (notascii) {
277
0
      return result(error_code::TOO_LARGE,
278
0
                    buf - buf_orig + _tzcnt_u64(notascii));
279
0
    }
280
0
  }
281
0
  return result(error_code::SUCCESS, len);
282
0
}
283
#endif // SIMDUTF_FEATURE_ASCII
284
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
285
simdutf_warn_unused bool
286
implementation::validate_utf16le_as_ascii(const char16_t *buf,
287
0
                                          size_t len) const noexcept {
288
0
  const char16_t *end = buf + len;
289
0
  __m512i limit = _mm512_set1_epi16(uint16_t(0x007F));
290
0
  for (; end - buf >= 32;) {
291
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
292
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
293
0
    if (mask) {
294
0
      return false;
295
0
    }
296
0
    buf += 32;
297
0
  }
298
0
  if (buf < end) {
299
0
    __m512i in =
300
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
301
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
302
0
    if (mask) {
303
0
      return false;
304
0
    }
305
0
  }
306
0
  return true;
307
0
}
308
309
simdutf_warn_unused bool
310
implementation::validate_utf16be_as_ascii(const char16_t *buf,
311
0
                                          size_t len) const noexcept {
312
0
  const char16_t *end = buf + len;
313
0
  const __m512i byteflip = _mm512_setr_epi64(
314
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
315
0
      0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
316
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809);
317
0
  __m512i limit = _mm512_set1_epi16(uint16_t(0x007F));
318
0
  for (; end - buf >= 32;) {
319
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
320
0
    in = _mm512_shuffle_epi8(in, byteflip);
321
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
322
0
    if (mask) {
323
0
      return false;
324
0
    }
325
0
    buf += 32;
326
0
  }
327
0
  if (buf < end) {
328
0
    __m512i in =
329
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
330
0
    in = _mm512_shuffle_epi8(in, byteflip);
331
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
332
0
    if (mask) {
333
0
      return false;
334
0
    }
335
0
  }
336
0
  return true;
337
0
}
338
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
339
#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
340
simdutf_warn_unused bool
341
implementation::validate_utf16le(const char16_t *buf,
342
0
                                 size_t len) const noexcept {
343
0
  const char16_t *end = buf + len;
344
345
  // Optimized: Process 64 code units (2x 512-bit) per iteration
346
0
  const __m512i surr_base = _mm512_set1_epi16(uint16_t(0xD800));
347
0
  const __m512i surr_range = _mm512_set1_epi16(uint16_t(0x0800));
348
0
  const __m512i high_range = _mm512_set1_epi16(uint16_t(0x0400));
349
350
0
  for (; end - buf >= 64;) {
351
0
    __m512i in_1 = _mm512_loadu_si512((__m512i *)buf);
352
0
    __m512i in_2 = _mm512_loadu_si512((__m512i *)(buf + 32));
353
354
0
    __m512i diff_1 = _mm512_sub_epi16(in_1, surr_base);
355
0
    __m512i diff_2 = _mm512_sub_epi16(in_2, surr_base);
356
357
0
    __mmask32 surrogates_1 = _mm512_cmplt_epu16_mask(diff_1, surr_range);
358
0
    __mmask32 surrogates_2 = _mm512_cmplt_epu16_mask(diff_2, surr_range);
359
360
0
    if (surrogates_1 | surrogates_2) {
361
0
      __mmask32 highsurrogates_1 = _mm512_cmplt_epu16_mask(diff_1, high_range);
362
0
      __mmask32 lowsurrogates_1 = surrogates_1 ^ highsurrogates_1;
363
364
0
      __mmask32 highsurrogates_2 = _mm512_cmplt_epu16_mask(diff_2, high_range);
365
0
      __mmask32 lowsurrogates_2 = surrogates_2 ^ highsurrogates_2;
366
367
      // Validate first block: high must be followed by low
368
0
      if ((highsurrogates_1 << 1) != lowsurrogates_1) {
369
0
        return false;
370
0
      }
371
372
      // Check boundary between blocks: if first block ends with high, second
373
      // must start with low
374
0
      bool ends_with_high_1 = ((highsurrogates_1 & 0x80000000) != 0);
375
0
      bool starts_with_low_2 = ((lowsurrogates_2 & 0x1) != 0);
376
0
      if (ends_with_high_1 && !starts_with_low_2) {
377
0
        return false;
378
0
      }
379
380
      // Validate second block (shift by 1 if first ended with high)
381
0
      __mmask32 expected_low_2 = ends_with_high_1
382
0
                                     ? (highsurrogates_2 << 1) | 0x1
383
0
                                     : (highsurrogates_2 << 1);
384
0
      if (expected_low_2 != lowsurrogates_2) {
385
0
        return false;
386
0
      }
387
388
0
      bool ends_with_high_2 = ((highsurrogates_2 & 0x80000000) != 0);
389
0
      if (ends_with_high_2) {
390
0
        buf += 63; // advance by 63 to start with high surrogate next round
391
0
      } else {
392
0
        buf += 64;
393
0
      }
394
0
    } else {
395
0
      buf += 64;
396
0
    }
397
0
  }
398
399
  // Handle remaining 32-63 code units
400
0
  for (; end - buf >= 32;) {
401
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
402
0
    __m512i diff = _mm512_sub_epi16(in, surr_base);
403
0
    __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, surr_range);
404
0
    if (surrogates) {
405
0
      __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, high_range);
406
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
407
      // high must be followed by low
408
0
      if ((highsurrogates << 1) != lowsurrogates) {
409
0
        return false;
410
0
      }
411
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
412
0
      if (ends_with_high) {
413
0
        buf += 31; // advance only by 31 code units so that we start with the
414
                   // high surrogate on the next round.
415
0
      } else {
416
0
        buf += 32;
417
0
      }
418
0
    } else {
419
0
      buf += 32;
420
0
    }
421
0
  }
422
0
  if (buf < end) {
423
0
    __m512i in =
424
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
425
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
426
0
    __mmask32 surrogates =
427
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
428
0
    if (surrogates) {
429
0
      __mmask32 highsurrogates =
430
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
431
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
432
      // high must be followed by low
433
0
      if ((highsurrogates << 1) != lowsurrogates) {
434
0
        return false;
435
0
      }
436
0
    }
437
0
  }
438
0
  return true;
439
0
}
440
#endif // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
441
442
#if SIMDUTF_FEATURE_UTF16
443
simdutf_warn_unused bool
444
implementation::validate_utf16be(const char16_t *buf,
445
0
                                 size_t len) const noexcept {
446
0
  const char16_t *end = buf + len;
447
448
0
  for (; end - buf >= 32;) {
449
0
    __m512i in = _mm512_slli_epi32(_mm512_loadu_si512((__m512i *)buf), 8);
450
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
451
0
    __mmask32 surrogates =
452
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
453
0
    if (surrogates) {
454
0
      __mmask32 highsurrogates =
455
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
456
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
457
      // high must be followed by low
458
0
      if ((highsurrogates << 1) != lowsurrogates) {
459
0
        return false;
460
0
      }
461
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
462
0
      if (ends_with_high) {
463
0
        buf += 31; // advance only by 31 code units so that we start with the
464
                   // high surrogate on the next round.
465
0
      } else {
466
0
        buf += 32;
467
0
      }
468
0
    } else {
469
0
      buf += 32;
470
0
    }
471
0
  }
472
0
  if (buf < end) {
473
0
    __m512i in = _mm512_slli_epi16(
474
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf), 8);
475
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
476
0
    __mmask32 surrogates =
477
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
478
0
    if (surrogates) {
479
0
      __mmask32 highsurrogates =
480
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
481
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
482
      // high must be followed by low
483
0
      if ((highsurrogates << 1) != lowsurrogates) {
484
0
        return false;
485
0
      }
486
0
    }
487
0
  }
488
0
  return true;
489
0
}
490
491
simdutf_warn_unused result implementation::validate_utf16le_with_errors(
492
0
    const char16_t *buf, size_t len) const noexcept {
493
0
  const char16_t *start_buf = buf;
494
0
  const char16_t *end = buf + len;
495
0
  for (; end - buf >= 32;) {
496
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
497
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
498
0
    __mmask32 surrogates =
499
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
500
0
    if (surrogates) {
501
0
      __mmask32 highsurrogates =
502
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
503
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
504
      // high must be followed by low
505
0
      if ((highsurrogates << 1) != lowsurrogates) {
506
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
507
0
        uint32_t extra_high =
508
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
509
0
        return result(error_code::SURROGATE,
510
0
                      (buf - start_buf) +
511
0
                          (extra_low < extra_high ? extra_low : extra_high));
512
0
      }
513
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
514
0
      if (ends_with_high) {
515
0
        buf += 31; // advance only by 31 code units so that we start with the
516
                   // high surrogate on the next round.
517
0
      } else {
518
0
        buf += 32;
519
0
      }
520
0
    } else {
521
0
      buf += 32;
522
0
    }
523
0
  }
524
0
  if (buf < end) {
525
0
    __m512i in =
526
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
527
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
528
0
    __mmask32 surrogates =
529
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
530
0
    if (surrogates) {
531
0
      __mmask32 highsurrogates =
532
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
533
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
534
      // high must be followed by low
535
0
      if ((highsurrogates << 1) != lowsurrogates) {
536
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
537
0
        uint32_t extra_high =
538
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
539
0
        return result(error_code::SURROGATE,
540
0
                      (buf - start_buf) +
541
0
                          (extra_low < extra_high ? extra_low : extra_high));
542
0
      }
543
0
    }
544
0
  }
545
0
  return result(error_code::SUCCESS, len);
546
0
}
547
548
simdutf_warn_unused result implementation::validate_utf16be_with_errors(
549
0
    const char16_t *buf, size_t len) const noexcept {
550
0
  const char16_t *start_buf = buf;
551
0
  const char16_t *end = buf + len;
552
553
0
  for (; end - buf >= 32;) {
554
0
    __m512i in = _mm512_slli_epi16(_mm512_loadu_si512((__m512i *)buf), 8);
555
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
556
0
    __mmask32 surrogates =
557
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
558
0
    if (surrogates) {
559
0
      __mmask32 highsurrogates =
560
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
561
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
562
      // high must be followed by low
563
0
      if ((highsurrogates << 1) != lowsurrogates) {
564
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
565
0
        uint32_t extra_high =
566
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
567
0
        return result(error_code::SURROGATE,
568
0
                      (buf - start_buf) +
569
0
                          (extra_low < extra_high ? extra_low : extra_high));
570
0
      }
571
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
572
0
      if (ends_with_high) {
573
0
        buf += 31; // advance only by 31 code units so that we start with the
574
                   // high surrogate on the next round.
575
0
      } else {
576
0
        buf += 32;
577
0
      }
578
0
    } else {
579
0
      buf += 32;
580
0
    }
581
0
  }
582
0
  if (buf < end) {
583
0
    __m512i in = _mm512_slli_epi16(
584
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf), 8);
585
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
586
0
    __mmask32 surrogates =
587
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
588
0
    if (surrogates) {
589
0
      __mmask32 highsurrogates =
590
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
591
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
592
      // high must be followed by low
593
0
      if ((highsurrogates << 1) != lowsurrogates) {
594
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
595
0
        uint32_t extra_high =
596
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
597
0
        return result(error_code::SURROGATE,
598
0
                      (buf - start_buf) +
599
0
                          (extra_low < extra_high ? extra_low : extra_high));
600
0
      }
601
0
    }
602
0
  }
603
0
  return result(error_code::SUCCESS, len);
604
0
}
605
606
void implementation::to_well_formed_utf16le(const char16_t *input, size_t len,
607
0
                                            char16_t *output) const noexcept {
608
0
  return utf16fix_avx512<endianness::LITTLE>(input, len, output);
609
0
}
610
611
void implementation::to_well_formed_utf16be(const char16_t *input, size_t len,
612
0
                                            char16_t *output) const noexcept {
613
0
  return utf16fix_avx512<endianness::BIG>(input, len, output);
614
0
}
615
#endif // SIMDUTF_FEATURE_UTF16
616
617
#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
618
simdutf_warn_unused bool
619
0
implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
620
0
  return icelake::validate_utf32(buf, len);
621
0
}
622
#endif // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
623
624
#if SIMDUTF_FEATURE_UTF32
625
simdutf_warn_unused result implementation::validate_utf32_with_errors(
626
0
    const char32_t *buf, size_t len) const noexcept {
627
0
  const char32_t *buf_orig = buf;
628
0
  if (len >= 16) {
629
0
    const char32_t *end = buf + len - 16;
630
0
    while (buf <= end) {
631
0
      __m512i utf32 = _mm512_loadu_si512((const __m512i *)buf);
632
0
      __mmask16 outside_range = _mm512_cmp_epu32_mask(
633
0
          utf32, _mm512_set1_epi32(0x10ffff), _MM_CMPINT_GT);
634
635
0
      __m512i utf32_off =
636
0
          _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
637
638
0
      __mmask16 surrogate_range = _mm512_cmp_epu32_mask(
639
0
          utf32_off, _mm512_set1_epi32(0xfffff7ff), _MM_CMPINT_GT);
640
0
      if ((outside_range | surrogate_range)) {
641
0
        auto outside_idx = _tzcnt_u32(outside_range);
642
0
        auto surrogate_idx = _tzcnt_u32(surrogate_range);
643
644
0
        if (outside_idx < surrogate_idx) {
645
0
          return result(error_code::TOO_LARGE, buf - buf_orig + outside_idx);
646
0
        }
647
648
0
        return result(error_code::SURROGATE, buf - buf_orig + surrogate_idx);
649
0
      }
650
651
0
      buf += 16;
652
0
    }
653
0
  }
654
0
  if (len > 0) {
655
0
    __m512i utf32 = _mm512_maskz_loadu_epi32(
656
0
        __mmask16((1U << (buf_orig + len - buf)) - 1), (const __m512i *)buf);
657
0
    __mmask16 outside_range = _mm512_cmp_epu32_mask(
658
0
        utf32, _mm512_set1_epi32(0x10ffff), _MM_CMPINT_GT);
659
0
    __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
660
661
0
    __mmask16 surrogate_range = _mm512_cmp_epu32_mask(
662
0
        utf32_off, _mm512_set1_epi32(0xfffff7ff), _MM_CMPINT_GT);
663
0
    if ((outside_range | surrogate_range)) {
664
0
      auto outside_idx = _tzcnt_u32(outside_range);
665
0
      auto surrogate_idx = _tzcnt_u32(surrogate_range);
666
667
0
      if (outside_idx < surrogate_idx) {
668
0
        return result(error_code::TOO_LARGE, buf - buf_orig + outside_idx);
669
0
      }
670
671
0
      return result(error_code::SURROGATE, buf - buf_orig + surrogate_idx);
672
0
    }
673
0
  }
674
675
0
  return result(error_code::SUCCESS, len);
676
0
}
677
#endif // SIMDUTF_FEATURE_UTF32
678
679
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
680
simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(
681
0
    const char *buf, size_t len, char *utf8_output) const noexcept {
682
0
  return icelake::latin1_to_utf8_avx512_start(buf, len, utf8_output);
683
0
}
684
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
685
686
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
687
simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(
688
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
689
0
  return icelake_convert_latin1_to_utf16<endianness::LITTLE>(buf, len,
690
0
                                                             utf16_output);
691
0
}
692
693
simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(
694
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
695
0
  return icelake_convert_latin1_to_utf16<endianness::BIG>(buf, len,
696
0
                                                          utf16_output);
697
0
}
698
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
699
700
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
701
simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(
702
0
    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
703
0
  avx512_convert_latin1_to_utf32(buf, len, utf32_output);
704
0
  return len;
705
0
}
706
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
707
708
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
709
simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(
710
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
711
0
  return icelake::utf8_to_latin1_avx512(buf, len, latin1_output);
712
0
}
713
714
simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(
715
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
716
  // First, try to convert as much as possible using the SIMD implementation.
717
0
  const char *obuf = buf;
718
0
  char *olatin1_output = latin1_output;
719
0
  size_t written = icelake::utf8_to_latin1_avx512(obuf, len, olatin1_output);
720
721
  // If we have completely converted the string
722
0
  if (obuf == buf + len) {
723
0
    return {simdutf::SUCCESS, written};
724
0
  }
725
0
  size_t pos = obuf - buf;
726
0
  result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
727
0
      pos, buf + pos, len - pos, latin1_output);
728
0
  res.count += pos;
729
0
  return res;
730
0
}
731
732
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(
733
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
734
0
  return icelake::valid_utf8_to_latin1_avx512(buf, len, latin1_output);
735
0
}
736
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
737
738
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
739
simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(
740
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
741
0
  utf8_to_utf16_result ret =
742
0
      fast_avx512_convert_utf8_to_utf16<endianness::LITTLE>(buf, len,
743
0
                                                            utf16_output);
744
0
  if (ret.second == nullptr) {
745
0
    return 0;
746
0
  }
747
0
  return ret.second - utf16_output;
748
0
}
749
750
simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(
751
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
752
0
  utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16<endianness::BIG>(
753
0
      buf, len, utf16_output);
754
0
  if (ret.second == nullptr) {
755
0
    return 0;
756
0
  }
757
0
  return ret.second - utf16_output;
758
0
}
759
760
simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(
761
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
762
0
  return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::LITTLE>(
763
0
      buf, len, utf16_output);
764
0
}
765
766
simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(
767
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
768
0
  return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::BIG>(
769
0
      buf, len, utf16_output);
770
0
}
771
772
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(
773
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
774
0
  utf8_to_utf16_result ret =
775
0
      icelake::valid_utf8_to_fixed_length<endianness::LITTLE, char16_t>(
776
0
          buf, len, utf16_output);
777
0
  size_t saved_bytes = ret.second - utf16_output;
778
0
  const char *end = buf + len;
779
0
  if (ret.first == end) {
780
0
    return saved_bytes;
781
0
  }
782
783
  // Note: AVX512 procedure looks up 4 bytes forward, and
784
  //       correctly converts multi-byte chars even if their
785
  //       continuation bytes lie outsiede 16-byte window.
786
  //       It meas, we have to skip continuation bytes from
787
  //       the beginning ret.first, as they were already consumed.
788
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
789
0
    ret.first += 1;
790
0
  }
791
792
0
  if (ret.first != end) {
793
0
    const size_t scalar_saved_bytes =
794
0
        scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(
795
0
            ret.first, len - (ret.first - buf), ret.second);
796
0
    if (scalar_saved_bytes == 0) {
797
0
      return 0;
798
0
    }
799
0
    saved_bytes += scalar_saved_bytes;
800
0
  }
801
802
0
  return saved_bytes;
803
0
}
804
805
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(
806
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
807
0
  utf8_to_utf16_result ret =
808
0
      icelake::valid_utf8_to_fixed_length<endianness::BIG, char16_t>(
809
0
          buf, len, utf16_output);
810
0
  size_t saved_bytes = ret.second - utf16_output;
811
0
  const char *end = buf + len;
812
0
  if (ret.first == end) {
813
0
    return saved_bytes;
814
0
  }
815
816
  // Note: AVX512 procedure looks up 4 bytes forward, and
817
  //       correctly converts multi-byte chars even if their
818
  //       continuation bytes lie outsiede 16-byte window.
819
  //       It meas, we have to skip continuation bytes from
820
  //       the beginning ret.first, as they were already consumed.
821
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
822
0
    ret.first += 1;
823
0
  }
824
825
0
  if (ret.first != end) {
826
0
    const size_t scalar_saved_bytes =
827
0
        scalar::utf8_to_utf16::convert_valid<endianness::BIG>(
828
0
            ret.first, len - (ret.first - buf), ret.second);
829
0
    if (scalar_saved_bytes == 0) {
830
0
      return 0;
831
0
    }
832
0
    saved_bytes += scalar_saved_bytes;
833
0
  }
834
835
0
  return saved_bytes;
836
0
}
837
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
838
839
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
840
simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(
841
0
    const char *buf, size_t len, char32_t *utf32_out) const noexcept {
842
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
843
0
  utf8_to_utf32_result ret =
844
0
      icelake::validating_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(
845
0
          buf, len, utf32_output);
846
0
  if (ret.second == nullptr)
847
0
    return 0;
848
849
0
  size_t saved_bytes = ret.second - utf32_output;
850
0
  const char *end = buf + len;
851
0
  if (ret.first == end) {
852
0
    return saved_bytes;
853
0
  }
854
855
  // Note: the AVX512 procedure looks up 4 bytes forward, and
856
  //       correctly converts multi-byte chars even if their
857
  //       continuation bytes lie outside 16-byte window.
858
  //       It means, we have to skip continuation bytes from
859
  //       the beginning ret.first, as they were already consumed.
860
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
861
0
    ret.first += 1;
862
0
  }
863
0
  if (ret.first != end) {
864
0
    const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert(
865
0
        ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
866
0
    if (scalar_saved_bytes == 0) {
867
0
      return 0;
868
0
    }
869
0
    saved_bytes += scalar_saved_bytes;
870
0
  }
871
872
0
  return saved_bytes;
873
0
}
874
875
simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(
876
0
    const char *buf, size_t len, char32_t *utf32) const noexcept {
877
0
  if (simdutf_unlikely(len == 0)) {
878
0
    return {error_code::SUCCESS, 0};
879
0
  }
880
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32);
881
0
  auto ret = icelake::validating_utf8_to_fixed_length_with_constant_checks<
882
0
      endianness::LITTLE, uint32_t>(buf, len, utf32_output);
883
884
0
  if (!std::get<2>(ret)) {
885
0
    size_t pos = std::get<0>(ret) - buf;
886
    // We might have an error that occurs right before  pos.
887
    // This is only a concern if buf[pos] is not a continuation byte.
888
0
    if ((buf[pos] & 0xc0) != 0x80 && pos >= 64) {
889
0
      pos -= 1;
890
0
    } else if ((buf[pos] & 0xc0) == 0x80 && pos >= 64) {
891
      // We must check whether we are the fourth continuation byte
892
0
      bool c1 = (buf[pos - 1] & 0xc0) == 0x80;
893
0
      bool c2 = (buf[pos - 2] & 0xc0) == 0x80;
894
0
      bool c3 = (buf[pos - 3] & 0xc0) == 0x80;
895
0
      if (c1 && c2 && c3) {
896
0
        return {simdutf::TOO_LONG, pos};
897
0
      }
898
0
    }
899
    // todo: we reset the output to utf32 instead of using std::get<2.(ret) as
900
    // you'd expect. that is because
901
    // validating_utf8_to_fixed_length_with_constant_checks may have processed
902
    // data beyond the error.
903
0
    result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
904
0
        pos, buf + pos, len - pos, utf32);
905
0
    res.count += pos;
906
0
    return res;
907
0
  }
908
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
909
0
  const char *end = buf + len;
910
0
  if (std::get<0>(ret) == end) {
911
0
    return {simdutf::SUCCESS, saved_bytes};
912
0
  }
913
914
  // Note: the AVX512 procedure looks up 4 bytes forward, and
915
  //       correctly converts multi-byte chars even if their
916
  //       continuation bytes lie outside 16-byte window.
917
  //       It means, we have to skip continuation bytes from
918
  //       the beginning ret.first, as they were already consumed.
919
0
  while (std::get<0>(ret) != end and
920
0
         ((uint8_t(*std::get<0>(ret)) & 0xc0) == 0x80)) {
921
0
    std::get<0>(ret) += 1;
922
0
  }
923
924
0
  if (std::get<0>(ret) != end) {
925
0
    auto scalar_result = scalar::utf8_to_utf32::convert_with_errors(
926
0
        std::get<0>(ret), len - (std::get<0>(ret) - buf),
927
0
        reinterpret_cast<char32_t *>(utf32_output) + saved_bytes);
928
0
    if (scalar_result.error != simdutf::SUCCESS) {
929
0
      scalar_result.count += (std::get<0>(ret) - buf);
930
0
    } else {
931
0
      scalar_result.count += saved_bytes;
932
0
    }
933
0
    return scalar_result;
934
0
  }
935
936
0
  return {simdutf::SUCCESS, size_t(std::get<1>(ret) - utf32_output)};
937
0
}
938
939
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(
940
0
    const char *buf, size_t len, char32_t *utf32_out) const noexcept {
941
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
942
0
  utf8_to_utf32_result ret =
943
0
      icelake::valid_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(
944
0
          buf, len, utf32_output);
945
0
  size_t saved_bytes = ret.second - utf32_output;
946
0
  const char *end = buf + len;
947
0
  if (ret.first == end) {
948
0
    return saved_bytes;
949
0
  }
950
951
  // Note: AVX512 procedure looks up 4 bytes forward, and
952
  //       correctly converts multi-byte chars even if their
953
  //       continuation bytes lie outsiede 16-byte window.
954
  //       It meas, we have to skip continuation bytes from
955
  //       the beginning ret.first, as they were already consumed.
956
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
957
0
    ret.first += 1;
958
0
  }
959
960
0
  if (ret.first != end) {
961
0
    const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert_valid(
962
0
        ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
963
0
    if (scalar_saved_bytes == 0) {
964
0
      return 0;
965
0
    }
966
0
    saved_bytes += scalar_saved_bytes;
967
0
  }
968
969
0
  return saved_bytes;
970
0
}
971
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
972
973
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
974
simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(
975
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
976
0
  return icelake_convert_utf16_to_latin1<endianness::LITTLE>(buf, len,
977
0
                                                             latin1_output);
978
0
}
979
980
simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(
981
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
982
0
  return icelake_convert_utf16_to_latin1<endianness::BIG>(buf, len,
983
0
                                                          latin1_output);
984
0
}
985
986
simdutf_warn_unused result
987
implementation::convert_utf16le_to_latin1_with_errors(
988
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
989
0
  return icelake_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
990
0
             buf, len, latin1_output)
991
0
      .first;
992
0
}
993
994
simdutf_warn_unused result
995
implementation::convert_utf16be_to_latin1_with_errors(
996
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
997
0
  return icelake_convert_utf16_to_latin1_with_errors<endianness::BIG>(
998
0
             buf, len, latin1_output)
999
0
      .first;
1000
0
}
1001
1002
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(
1003
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1004
  // optimization opportunity: implement custom function
1005
0
  return convert_utf16be_to_latin1(buf, len, latin1_output);
1006
0
}
1007
1008
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(
1009
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1010
  // optimization opportunity: implement custom function
1011
0
  return convert_utf16le_to_latin1(buf, len, latin1_output);
1012
0
}
1013
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
1014
1015
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1016
simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(
1017
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1018
0
  size_t outlen;
1019
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::LITTLE>(
1020
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1021
0
  if (inlen != len) {
1022
0
    return 0;
1023
0
  }
1024
0
  return outlen;
1025
0
}
1026
1027
simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(
1028
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1029
0
  size_t outlen;
1030
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::BIG>(
1031
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1032
0
  if (inlen != len) {
1033
0
    return 0;
1034
0
  }
1035
0
  return outlen;
1036
0
}
1037
1038
simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(
1039
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1040
0
  size_t outlen;
1041
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::LITTLE>(
1042
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1043
0
  if (inlen != len) {
1044
0
    result res = scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
1045
0
        buf + inlen, len - inlen, utf8_output + outlen);
1046
0
    res.count += inlen;
1047
0
    return res;
1048
0
  }
1049
0
  return {simdutf::SUCCESS, outlen};
1050
0
}
1051
1052
simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(
1053
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1054
0
  size_t outlen;
1055
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::BIG>(
1056
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1057
0
  if (inlen != len) {
1058
0
    result res = scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
1059
0
        buf + inlen, len - inlen, utf8_output + outlen);
1060
0
    res.count += inlen;
1061
0
    return res;
1062
0
  }
1063
0
  return {simdutf::SUCCESS, outlen};
1064
0
}
1065
1066
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(
1067
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1068
0
  return convert_utf16le_to_utf8(buf, len, utf8_output);
1069
0
}
1070
1071
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(
1072
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1073
0
  return convert_utf16be_to_utf8(buf, len, utf8_output);
1074
0
}
1075
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1076
1077
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
1078
simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(
1079
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1080
0
  return icelake_convert_utf32_to_latin1(buf, len, latin1_output);
1081
0
}
1082
1083
simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(
1084
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1085
0
  return icelake_convert_utf32_to_latin1_with_errors(buf, len, latin1_output)
1086
0
      .first;
1087
0
}
1088
1089
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(
1090
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1091
0
  return icelake_convert_utf32_to_latin1(buf, len, latin1_output);
1092
0
}
1093
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
1094
1095
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1096
simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(
1097
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1098
0
  std::pair<const char32_t *, char *> ret =
1099
0
      avx512_convert_utf32_to_utf8(buf, len, utf8_output);
1100
0
  if (ret.first == nullptr) {
1101
0
    return 0;
1102
0
  }
1103
0
  size_t saved_bytes = ret.second - utf8_output;
1104
0
  if (ret.first != buf + len) {
1105
0
    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
1106
0
        ret.first, len - (ret.first - buf), ret.second);
1107
0
    if (scalar_saved_bytes == 0) {
1108
0
      return 0;
1109
0
    }
1110
0
    saved_bytes += scalar_saved_bytes;
1111
0
  }
1112
0
  return saved_bytes;
1113
0
}
1114
1115
simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(
1116
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1117
  // ret.first.count is always the position in the buffer, not the number of
1118
  // code units written even if finished
1119
0
  std::pair<result, char *> ret =
1120
0
      icelake::avx512_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
1121
0
  if (ret.first.count != len) {
1122
0
    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
1123
0
        buf + ret.first.count, len - ret.first.count, ret.second);
1124
0
    if (scalar_res.error) {
1125
0
      scalar_res.count += ret.first.count;
1126
0
      return scalar_res;
1127
0
    } else {
1128
0
      ret.second += scalar_res.count;
1129
0
    }
1130
0
  }
1131
0
  ret.first.count =
1132
0
      ret.second -
1133
0
      utf8_output; // Set count to the number of 8-bit code units written
1134
0
  return ret.first;
1135
0
}
1136
1137
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(
1138
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1139
0
  return convert_utf32_to_utf8(buf, len, utf8_output);
1140
0
}
1141
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1142
1143
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1144
simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(
1145
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1146
0
  std::pair<const char32_t *, char16_t *> ret =
1147
0
      avx512_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
1148
0
  if (ret.first == nullptr) {
1149
0
    return 0;
1150
0
  }
1151
0
  size_t saved_bytes = ret.second - utf16_output;
1152
0
  return saved_bytes;
1153
0
}
1154
1155
simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(
1156
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1157
0
  std::pair<const char32_t *, char16_t *> ret =
1158
0
      avx512_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
1159
0
  if (ret.first == nullptr) {
1160
0
    return 0;
1161
0
  }
1162
0
  size_t saved_bytes = ret.second - utf16_output;
1163
0
  return saved_bytes;
1164
0
}
1165
1166
simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(
1167
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1168
  // ret.first.count is always the position in the buffer, not the number of
1169
  // code units written even if finished
1170
0
  std::pair<result, char16_t *> ret =
1171
0
      avx512_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
1172
0
          buf, len, utf16_output);
1173
0
  if (ret.first.error) {
1174
0
    return ret.first;
1175
0
  }
1176
0
  ret.first.count =
1177
0
      ret.second -
1178
0
      utf16_output; // Set count to the number of 8-bit code units written
1179
0
  return ret.first;
1180
0
}
1181
1182
simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(
1183
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1184
  // ret.first.count is always the position in the buffer, not the number of
1185
  // code units written even if finished
1186
0
  std::pair<result, char16_t *> ret =
1187
0
      avx512_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len,
1188
0
                                                                 utf16_output);
1189
0
  if (ret.first.error) {
1190
0
    return ret.first;
1191
0
  }
1192
0
  ret.first.count =
1193
0
      ret.second -
1194
0
      utf16_output; // Set count to the number of 8-bit code units written
1195
0
  return ret.first;
1196
0
}
1197
1198
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(
1199
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1200
0
  return convert_utf32_to_utf16le(buf, len, utf16_output);
1201
0
}
1202
1203
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(
1204
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1205
0
  return convert_utf32_to_utf16be(buf, len, utf16_output);
1206
0
}
1207
1208
simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(
1209
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1210
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1211
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1212
0
                                                          utf32_output);
1213
0
  if (!std::get<2>(ret)) {
1214
0
    return 0;
1215
0
  }
1216
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1217
0
  if (std::get<0>(ret) != buf + len) {
1218
0
    const size_t scalar_saved_bytes =
1219
0
        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
1220
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1221
0
    if (scalar_saved_bytes == 0) {
1222
0
      return 0;
1223
0
    }
1224
0
    saved_bytes += scalar_saved_bytes;
1225
0
  }
1226
0
  return saved_bytes;
1227
0
}
1228
1229
simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(
1230
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1231
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1232
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1233
0
  if (!std::get<2>(ret)) {
1234
0
    return 0;
1235
0
  }
1236
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1237
0
  if (std::get<0>(ret) != buf + len) {
1238
0
    const size_t scalar_saved_bytes =
1239
0
        scalar::utf16_to_utf32::convert<endianness::BIG>(
1240
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1241
0
    if (scalar_saved_bytes == 0) {
1242
0
      return 0;
1243
0
    }
1244
0
    saved_bytes += scalar_saved_bytes;
1245
0
  }
1246
0
  return saved_bytes;
1247
0
}
1248
1249
simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(
1250
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1251
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1252
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1253
0
                                                          utf32_output);
1254
0
  if (!std::get<2>(ret)) {
1255
0
    result scalar_res =
1256
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
1257
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1258
0
    scalar_res.count += (std::get<0>(ret) - buf);
1259
0
    return scalar_res;
1260
0
  }
1261
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1262
0
  if (std::get<0>(ret) != buf + len) {
1263
0
    result scalar_res =
1264
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
1265
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1266
0
    if (scalar_res.error) {
1267
0
      scalar_res.count += (std::get<0>(ret) - buf);
1268
0
      return scalar_res;
1269
0
    } else {
1270
0
      scalar_res.count += saved_bytes;
1271
0
      return scalar_res;
1272
0
    }
1273
0
  }
1274
0
  return simdutf::result(simdutf::SUCCESS, saved_bytes);
1275
0
}
1276
1277
simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(
1278
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1279
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1280
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1281
0
  if (!std::get<2>(ret)) {
1282
0
    result scalar_res =
1283
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
1284
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1285
0
    scalar_res.count += (std::get<0>(ret) - buf);
1286
0
    return scalar_res;
1287
0
  }
1288
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1289
0
  if (std::get<0>(ret) != buf + len) {
1290
0
    result scalar_res =
1291
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
1292
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1293
0
    if (scalar_res.error) {
1294
0
      scalar_res.count += (std::get<0>(ret) - buf);
1295
0
      return scalar_res;
1296
0
    } else {
1297
0
      scalar_res.count += saved_bytes;
1298
0
      return scalar_res;
1299
0
    }
1300
0
  }
1301
0
  return simdutf::result(simdutf::SUCCESS, saved_bytes);
1302
0
}
1303
1304
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(
1305
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1306
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1307
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1308
0
                                                          utf32_output);
1309
0
  if (!std::get<2>(ret)) {
1310
0
    return 0;
1311
0
  }
1312
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1313
0
  if (std::get<0>(ret) != buf + len) {
1314
0
    const size_t scalar_saved_bytes =
1315
0
        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
1316
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1317
0
    if (scalar_saved_bytes == 0) {
1318
0
      return 0;
1319
0
    }
1320
0
    saved_bytes += scalar_saved_bytes;
1321
0
  }
1322
0
  return saved_bytes;
1323
0
}
1324
1325
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(
1326
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1327
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1328
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1329
0
  if (!std::get<2>(ret)) {
1330
0
    return 0;
1331
0
  }
1332
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1333
0
  if (std::get<0>(ret) != buf + len) {
1334
0
    const size_t scalar_saved_bytes =
1335
0
        scalar::utf16_to_utf32::convert<endianness::BIG>(
1336
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1337
0
    if (scalar_saved_bytes == 0) {
1338
0
      return 0;
1339
0
    }
1340
0
    saved_bytes += scalar_saved_bytes;
1341
0
  }
1342
0
  return saved_bytes;
1343
0
}
1344
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1345
1346
#if SIMDUTF_FEATURE_UTF16
1347
void implementation::change_endianness_utf16(const char16_t *input,
1348
                                             size_t length,
1349
0
                                             char16_t *output) const noexcept {
1350
0
  size_t pos = 0;
1351
0
  const __m512i byteflip = _mm512_setr_epi64(
1352
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
1353
0
      0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
1354
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809);
1355
0
  while (pos + 32 <= length) {
1356
0
    __m512i utf16 = _mm512_loadu_si512((const __m512i *)(input + pos));
1357
0
    utf16 = _mm512_shuffle_epi8(utf16, byteflip);
1358
0
    _mm512_storeu_si512(output + pos, utf16);
1359
0
    pos += 32;
1360
0
  }
1361
0
  if (pos < length) {
1362
0
    __mmask32 m((1U << (length - pos)) - 1);
1363
0
    __m512i utf16 = _mm512_maskz_loadu_epi16(m, (const __m512i *)(input + pos));
1364
0
    utf16 = _mm512_shuffle_epi8(utf16, byteflip);
1365
0
    _mm512_mask_storeu_epi16(output + pos, m, utf16);
1366
0
  }
1367
0
}
1368
1369
simdutf_warn_unused size_t implementation::count_utf16le(
1370
0
    const char16_t *input, size_t length) const noexcept {
1371
0
  const char16_t *ptr = input;
1372
0
  size_t count{0};
1373
1374
0
  if (length >= 32) {
1375
0
    const char16_t *end = input + length - 32;
1376
1377
0
    const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
1378
0
    const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
1379
1380
0
    while (ptr <= end) {
1381
0
      __m512i utf16 = _mm512_loadu_si512((const __m512i *)ptr);
1382
0
      ptr += 32;
1383
0
      uint64_t not_high_surrogate =
1384
0
          static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) |
1385
0
                                _mm512_cmplt_epu16_mask(utf16, low));
1386
0
      count += count_ones(not_high_surrogate);
1387
0
    }
1388
0
  }
1389
1390
0
  return count + scalar::utf16::count_code_points<endianness::LITTLE>(
1391
0
                     ptr, length - (ptr - input));
1392
0
}
1393
1394
simdutf_warn_unused size_t implementation::count_utf16be(
1395
0
    const char16_t *input, size_t length) const noexcept {
1396
0
  const char16_t *ptr = input;
1397
0
  size_t count{0};
1398
0
  if (length >= 32) {
1399
1400
0
    const char16_t *end = input + length - 32;
1401
1402
0
    const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
1403
0
    const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
1404
1405
0
    const __m512i byteflip = _mm512_setr_epi64(
1406
0
        0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
1407
0
        0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
1408
0
        0x0607040502030001, 0x0e0f0c0d0a0b0809);
1409
0
    while (ptr <= end) {
1410
0
      __m512i utf16 =
1411
0
          _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i *)ptr), byteflip);
1412
0
      ptr += 32;
1413
0
      uint64_t not_high_surrogate =
1414
0
          static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) |
1415
0
                                _mm512_cmplt_epu16_mask(utf16, low));
1416
0
      count += count_ones(not_high_surrogate);
1417
0
    }
1418
0
  }
1419
1420
0
  return count + scalar::utf16::count_code_points<endianness::BIG>(
1421
0
                     ptr, length - (ptr - input));
1422
0
}
1423
#endif // SIMDUTF_FEATURE_UTF16
1424
1425
#if SIMDUTF_FEATURE_UTF8
1426
simdutf_warn_unused size_t
1427
0
implementation::count_utf8(const char *input, size_t length) const noexcept {
1428
0
  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
1429
0
  size_t answer =
1430
0
      length / sizeof(__m512i) *
1431
0
      sizeof(__m512i); // Number of 512-bit chunks that fits into the length.
1432
0
  size_t i = 0;
1433
0
  __m512i unrolled_popcount{0};
1434
1435
0
  const __m512i continuation = _mm512_set1_epi8(char(0b10111111));
1436
1437
0
  while (i + sizeof(__m512i) <= length) {
1438
0
    size_t iterations = (length - i) / sizeof(__m512i);
1439
1440
0
    size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
1441
0
    for (; i + 8 * sizeof(__m512i) <= max_i; i += 8 * sizeof(__m512i)) {
1442
0
      __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
1443
0
      __m512i input2 =
1444
0
          _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
1445
0
      __m512i input3 =
1446
0
          _mm512_loadu_si512((const __m512i *)(str + i + 2 * sizeof(__m512i)));
1447
0
      __m512i input4 =
1448
0
          _mm512_loadu_si512((const __m512i *)(str + i + 3 * sizeof(__m512i)));
1449
0
      __m512i input5 =
1450
0
          _mm512_loadu_si512((const __m512i *)(str + i + 4 * sizeof(__m512i)));
1451
0
      __m512i input6 =
1452
0
          _mm512_loadu_si512((const __m512i *)(str + i + 5 * sizeof(__m512i)));
1453
0
      __m512i input7 =
1454
0
          _mm512_loadu_si512((const __m512i *)(str + i + 6 * sizeof(__m512i)));
1455
0
      __m512i input8 =
1456
0
          _mm512_loadu_si512((const __m512i *)(str + i + 7 * sizeof(__m512i)));
1457
1458
0
      __mmask64 mask1 = _mm512_cmple_epi8_mask(input1, continuation);
1459
0
      __mmask64 mask2 = _mm512_cmple_epi8_mask(input2, continuation);
1460
0
      __mmask64 mask3 = _mm512_cmple_epi8_mask(input3, continuation);
1461
0
      __mmask64 mask4 = _mm512_cmple_epi8_mask(input4, continuation);
1462
0
      __mmask64 mask5 = _mm512_cmple_epi8_mask(input5, continuation);
1463
0
      __mmask64 mask6 = _mm512_cmple_epi8_mask(input6, continuation);
1464
0
      __mmask64 mask7 = _mm512_cmple_epi8_mask(input7, continuation);
1465
0
      __mmask64 mask8 = _mm512_cmple_epi8_mask(input8, continuation);
1466
1467
0
      __m512i mask_register = _mm512_set_epi64(mask8, mask7, mask6, mask5,
1468
0
                                               mask4, mask3, mask2, mask1);
1469
1470
0
      unrolled_popcount = _mm512_add_epi64(unrolled_popcount,
1471
0
                                           _mm512_popcnt_epi64(mask_register));
1472
0
    }
1473
1474
0
    for (; i <= max_i; i += sizeof(__m512i)) {
1475
0
      __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
1476
0
      uint64_t continuation_bitmask = static_cast<uint64_t>(
1477
0
          _mm512_cmple_epi8_mask(more_input, continuation));
1478
0
      answer -= count_ones(continuation_bitmask);
1479
0
    }
1480
0
  }
1481
1482
0
  answer -= _mm512_reduce_add_epi64(unrolled_popcount);
1483
1484
0
  return answer + scalar::utf8::count_code_points(
1485
0
                      reinterpret_cast<const char *>(str + i), length - i);
1486
0
}
1487
#endif // SIMDUTF_FEATURE_UTF8
1488
1489
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1490
simdutf_warn_unused size_t implementation::latin1_length_from_utf8(
1491
0
    const char *buf, size_t len) const noexcept {
1492
0
  return count_utf8(buf, len);
1493
0
}
1494
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1495
1496
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1497
simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(
1498
0
    const char16_t *input, size_t length) const noexcept {
1499
0
  return icelake_utf8_length_from_utf16<endianness::LITTLE>(input, length);
1500
0
}
1501
1502
simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(
1503
0
    const char16_t *input, size_t length) const noexcept {
1504
0
  return icelake_utf8_length_from_utf16<endianness::BIG>(input, length);
1505
0
}
1506
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1507
1508
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1509
simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(
1510
0
    const char16_t *input, size_t length) const noexcept {
1511
0
  return implementation::count_utf16le(input, length);
1512
0
}
1513
1514
simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(
1515
0
    const char16_t *input, size_t length) const noexcept {
1516
0
  return implementation::count_utf16be(input, length);
1517
0
}
1518
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1519
1520
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1521
simdutf_warn_unused size_t implementation::utf8_length_from_latin1(
1522
0
    const char *input, size_t length) const noexcept {
1523
0
  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
1524
0
  size_t answer = length / sizeof(__m512i) * sizeof(__m512i);
1525
0
  size_t i = 0;
1526
0
  if (answer >= 2048) // long strings optimization
1527
0
  {
1528
0
    unsigned char v_0xFF = 0xff;
1529
0
    __m512i eight_64bits = _mm512_setzero_si512();
1530
0
    while (i + sizeof(__m512i) <= length) {
1531
0
      __m512i runner = _mm512_setzero_si512();
1532
0
      size_t iterations = (length - i) / sizeof(__m512i);
1533
0
      if (iterations > 255) {
1534
0
        iterations = 255;
1535
0
      }
1536
0
      size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
1537
0
      for (; i + 4 * sizeof(__m512i) <= max_i; i += 4 * sizeof(__m512i)) {
1538
        // Load four __m512i vectors
1539
0
        __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
1540
0
        __m512i input2 =
1541
0
            _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
1542
0
        __m512i input3 = _mm512_loadu_si512(
1543
0
            (const __m512i *)(str + i + 2 * sizeof(__m512i)));
1544
0
        __m512i input4 = _mm512_loadu_si512(
1545
0
            (const __m512i *)(str + i + 3 * sizeof(__m512i)));
1546
1547
        // Generate four masks
1548
0
        __mmask64 mask1 =
1549
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input1);
1550
0
        __mmask64 mask2 =
1551
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input2);
1552
0
        __mmask64 mask3 =
1553
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input3);
1554
0
        __mmask64 mask4 =
1555
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input4);
1556
        // Apply the masks and subtract from the runner
1557
0
        __m512i not_ascii1 =
1558
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask1, v_0xFF);
1559
0
        __m512i not_ascii2 =
1560
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask2, v_0xFF);
1561
0
        __m512i not_ascii3 =
1562
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask3, v_0xFF);
1563
0
        __m512i not_ascii4 =
1564
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask4, v_0xFF);
1565
1566
0
        runner = _mm512_sub_epi8(runner, not_ascii1);
1567
0
        runner = _mm512_sub_epi8(runner, not_ascii2);
1568
0
        runner = _mm512_sub_epi8(runner, not_ascii3);
1569
0
        runner = _mm512_sub_epi8(runner, not_ascii4);
1570
0
      }
1571
1572
0
      for (; i <= max_i; i += sizeof(__m512i)) {
1573
0
        __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
1574
1575
0
        __mmask64 mask =
1576
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), more_input);
1577
0
        __m512i not_ascii =
1578
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask, v_0xFF);
1579
0
        runner = _mm512_sub_epi8(runner, not_ascii);
1580
0
      }
1581
1582
0
      eight_64bits = _mm512_add_epi64(
1583
0
          eight_64bits, _mm512_sad_epu8(runner, _mm512_setzero_si512()));
1584
0
    }
1585
1586
0
    answer += _mm512_reduce_add_epi64(eight_64bits);
1587
0
  } else if (answer > 0) {
1588
0
    for (; i + sizeof(__m512i) <= length; i += sizeof(__m512i)) {
1589
0
      __m512i latin = _mm512_loadu_si512((const __m512i *)(str + i));
1590
0
      uint64_t non_ascii = _mm512_movepi8_mask(latin);
1591
0
      answer += count_ones(non_ascii);
1592
0
    }
1593
0
  }
1594
0
  return answer + scalar::latin1::utf8_length_from_latin1(
1595
0
                      reinterpret_cast<const char *>(str + i), length - i);
1596
0
}
1597
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1598
1599
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1600
simdutf_warn_unused size_t implementation::utf16_length_from_utf8(
1601
0
    const char *input, size_t length) const noexcept {
1602
0
  size_t pos = 0;
1603
1604
  // UTF-16 char length based on the four most significant bits of UTF-8 bytes
1605
0
  const __m128i utf8_length_128 = _mm_setr_epi8(
1606
      // ASCII chars
1607
0
      /* 0000 */ 1,
1608
0
      /* 0001 */ 1,
1609
0
      /* 0010 */ 1,
1610
0
      /* 0011 */ 1,
1611
0
      /* 0100 */ 1,
1612
0
      /* 0101 */ 1,
1613
0
      /* 0110 */ 1,
1614
0
      /* 0111 */ 1,
1615
1616
      // continuation bytes
1617
      /* 1000 */ 0,
1618
0
      /* 1001 */ 0,
1619
0
      /* 1010 */ 0,
1620
0
      /* 1011 */ 0,
1621
1622
      // leading bytes
1623
      /* 1100 */ 1, // 2-byte UTF-8 char => 1 UTF-16 word
1624
0
      /* 1101 */ 1, // 2-byte UTF-8 char => 1 UTF-16 word
1625
0
      /* 1110 */ 1, // 3-byte UTF-8 char => 1 UTF-16 word
1626
0
      /* 1111 */ 2  // 4-byte UTF-8 char => 2 UTF-16 words (surrogate pair)
1627
0
  );
1628
1629
0
  const __m512i char_length = broadcast_128bit_lane(utf8_length_128);
1630
1631
0
  constexpr size_t max_iterations = 255 / 2;
1632
1633
0
  size_t iterations = 0;
1634
0
  const auto zero = _mm512_setzero_si512();
1635
0
  __m512i local = _mm512_setzero_si512();    // byte-wise counters
1636
0
  __m512i counters = _mm512_setzero_si512(); // 64-bit counters
1637
0
  for (; pos + 64 <= length; pos += 64) {
1638
0
    __m512i utf8 = _mm512_loadu_si512((const __m512i *)(input + pos));
1639
0
    const auto t0 = _mm512_srli_epi32(utf8, 4);
1640
0
    const auto t1 = _mm512_and_si512(t0, _mm512_set1_epi8(0xf));
1641
0
    const auto t2 = _mm512_shuffle_epi8(char_length, t1);
1642
0
    local = _mm512_add_epi8(local, t2);
1643
1644
0
    iterations += 1;
1645
0
    if (iterations == max_iterations) {
1646
0
      counters = _mm512_add_epi64(counters, _mm512_sad_epu8(local, zero));
1647
0
      local = zero;
1648
0
      iterations = 0;
1649
0
    }
1650
0
  }
1651
1652
0
  size_t count = 0;
1653
1654
0
  if (pos > 0) {
1655
    // don't waste time for short strings
1656
0
    if (iterations > 0) {
1657
0
      counters = _mm512_add_epi64(counters, _mm512_sad_epu8(local, zero));
1658
0
    }
1659
1660
0
    const auto l0 = _mm512_extracti32x4_epi32(counters, 0);
1661
0
    const auto l1 = _mm512_extracti32x4_epi32(counters, 1);
1662
0
    const auto l2 = _mm512_extracti32x4_epi32(counters, 2);
1663
0
    const auto l3 = _mm512_extracti32x4_epi32(counters, 3);
1664
1665
0
    const auto sum =
1666
0
        _mm_add_epi64(_mm_add_epi64(l0, l1), _mm_add_epi64(l2, l3));
1667
1668
0
    count = uint64_t(_mm_extract_epi64(sum, 0)) +
1669
0
            uint64_t(_mm_extract_epi64(sum, 1));
1670
0
  }
1671
1672
0
  return count +
1673
0
         scalar::utf8::utf16_length_from_utf8(input + pos, length - pos);
1674
0
}
1675
simdutf_warn_unused result
1676
implementation::utf8_length_from_utf16le_with_replacement(
1677
0
    const char16_t *input, size_t length) const noexcept {
1678
0
  return icelake_utf8_length_from_utf16_with_replacement<endianness::LITTLE>(
1679
0
      input, length);
1680
0
}
1681
1682
simdutf_warn_unused result
1683
implementation::utf8_length_from_utf16be_with_replacement(
1684
0
    const char16_t *input, size_t length) const noexcept {
1685
0
  return icelake_utf8_length_from_utf16_with_replacement<endianness::BIG>(
1686
0
      input, length);
1687
0
}
1688
1689
simdutf_warn_unused size_t
1690
implementation::convert_utf16le_to_utf8_with_replacement(
1691
0
    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
1692
0
  return utf16_to_utf8::convert_with_replacement_via(
1693
0
      [this](const char16_t *b, size_t l, char *o) {
1694
0
        return this->convert_utf16le_to_utf8_with_errors(b, l, o);
1695
0
      },
1696
0
      [this](const char16_t *b, size_t l) {
1697
0
        return this->utf8_length_from_utf16le(b, l);
1698
0
      },
1699
0
      input, length, utf8_buffer);
1700
0
}
1701
1702
simdutf_warn_unused size_t
1703
implementation::convert_utf16be_to_utf8_with_replacement(
1704
0
    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
1705
0
  return utf16_to_utf8::convert_with_replacement_via(
1706
0
      [this](const char16_t *b, size_t l, char *o) {
1707
0
        return this->convert_utf16be_to_utf8_with_errors(b, l, o);
1708
0
      },
1709
0
      [this](const char16_t *b, size_t l) {
1710
0
        return this->utf8_length_from_utf16be(b, l);
1711
0
      },
1712
0
      input, length, utf8_buffer);
1713
0
}
1714
1715
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1716
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1717
simdutf_warn_unused size_t implementation::utf8_length_from_utf32(
1718
0
    const char32_t *input, size_t length) const noexcept {
1719
0
  return utf32::utf8_length_from_utf32(input, length);
1720
0
}
1721
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1722
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1723
simdutf_warn_unused size_t implementation::utf16_length_from_utf32(
1724
0
    const char32_t *input, size_t length) const noexcept {
1725
0
  const char32_t *ptr = input;
1726
0
  size_t count{0};
1727
1728
0
  if (length >= 16) {
1729
0
    const char32_t *end = input + length - 16;
1730
1731
0
    const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff);
1732
1733
0
    while (ptr <= end) {
1734
0
      __m512i utf32 = _mm512_loadu_si512((const __m512i *)ptr);
1735
0
      ptr += 16;
1736
0
      __mmask16 surrogates_bitmask =
1737
0
          _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff);
1738
1739
0
      count += 16 + count_ones(surrogates_bitmask);
1740
0
    }
1741
0
  }
1742
1743
0
  return count +
1744
0
         scalar::utf32::utf16_length_from_utf32(ptr, length - (ptr - input));
1745
0
}
1746
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1747
1748
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1749
simdutf_warn_unused size_t implementation::utf32_length_from_utf8(
1750
0
    const char *input, size_t length) const noexcept {
1751
0
  return implementation::count_utf8(input, length);
1752
0
}
1753
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1754
1755
#if SIMDUTF_FEATURE_BASE64
1756
simdutf_warn_unused result implementation::base64_to_binary(
1757
    const char *input, size_t length, char *output, base64_options options,
1758
0
    last_chunk_handling_options last_chunk_options) const noexcept {
1759
0
  if (options & base64_default_or_url) {
1760
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
1761
0
      return compress_decode_base64<false, true, true>(
1762
0
          output, input, length, options, last_chunk_options);
1763
0
    } else {
1764
0
      return compress_decode_base64<false, false, true>(
1765
0
          output, input, length, options, last_chunk_options);
1766
0
    }
1767
0
  } else if (options & base64_url) {
1768
0
    if (options == base64_options::base64_url_accept_garbage) {
1769
0
      return compress_decode_base64<true, true, false>(
1770
0
          output, input, length, options, last_chunk_options);
1771
0
    } else {
1772
0
      return compress_decode_base64<true, false, false>(
1773
0
          output, input, length, options, last_chunk_options);
1774
0
    }
1775
0
  } else {
1776
0
    if (options == base64_options::base64_default_accept_garbage) {
1777
0
      return compress_decode_base64<false, true, false>(
1778
0
          output, input, length, options, last_chunk_options);
1779
0
    } else {
1780
0
      return compress_decode_base64<false, false, false>(
1781
0
          output, input, length, options, last_chunk_options);
1782
0
    }
1783
0
  }
1784
0
}
1785
1786
simdutf_warn_unused full_result implementation::base64_to_binary_details(
1787
    const char *input, size_t length, char *output, base64_options options,
1788
0
    last_chunk_handling_options last_chunk_options) const noexcept {
1789
0
  if (options & base64_default_or_url) {
1790
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
1791
0
      return compress_decode_base64<false, true, true>(
1792
0
          output, input, length, options, last_chunk_options);
1793
0
    } else {
1794
0
      return compress_decode_base64<false, false, true>(
1795
0
          output, input, length, options, last_chunk_options);
1796
0
    }
1797
0
  } else if (options & base64_url) {
1798
0
    if (options == base64_options::base64_url_accept_garbage) {
1799
0
      return compress_decode_base64<true, true, false>(
1800
0
          output, input, length, options, last_chunk_options);
1801
0
    } else {
1802
0
      return compress_decode_base64<true, false, false>(
1803
0
          output, input, length, options, last_chunk_options);
1804
0
    }
1805
0
  } else {
1806
0
    if (options == base64_options::base64_default_accept_garbage) {
1807
0
      return compress_decode_base64<false, true, false>(
1808
0
          output, input, length, options, last_chunk_options);
1809
0
    } else {
1810
0
      return compress_decode_base64<false, false, false>(
1811
0
          output, input, length, options, last_chunk_options);
1812
0
    }
1813
0
  }
1814
0
}
1815
1816
simdutf_warn_unused result implementation::base64_to_binary(
1817
    const char16_t *input, size_t length, char *output, base64_options options,
1818
0
    last_chunk_handling_options last_chunk_options) const noexcept {
1819
0
  if (options & base64_default_or_url) {
1820
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
1821
0
      return compress_decode_base64<false, true, true>(
1822
0
          output, input, length, options, last_chunk_options);
1823
0
    } else {
1824
0
      return compress_decode_base64<false, false, true>(
1825
0
          output, input, length, options, last_chunk_options);
1826
0
    }
1827
0
  } else if (options & base64_url) {
1828
0
    if (options == base64_options::base64_url_accept_garbage) {
1829
0
      return compress_decode_base64<true, true, false>(
1830
0
          output, input, length, options, last_chunk_options);
1831
0
    } else {
1832
0
      return compress_decode_base64<true, false, false>(
1833
0
          output, input, length, options, last_chunk_options);
1834
0
    }
1835
0
  } else {
1836
0
    if (options == base64_options::base64_default_accept_garbage) {
1837
0
      return compress_decode_base64<false, true, false>(
1838
0
          output, input, length, options, last_chunk_options);
1839
0
    } else {
1840
0
      return compress_decode_base64<false, false, false>(
1841
0
          output, input, length, options, last_chunk_options);
1842
0
    }
1843
0
  }
1844
0
}
1845
1846
simdutf_warn_unused full_result implementation::base64_to_binary_details(
1847
    const char16_t *input, size_t length, char *output, base64_options options,
1848
0
    last_chunk_handling_options last_chunk_options) const noexcept {
1849
0
  if (options & base64_default_or_url) {
1850
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
1851
0
      return compress_decode_base64<false, true, true>(
1852
0
          output, input, length, options, last_chunk_options);
1853
0
    } else {
1854
0
      return compress_decode_base64<false, false, true>(
1855
0
          output, input, length, options, last_chunk_options);
1856
0
    }
1857
0
  } else if (options & base64_url) {
1858
0
    if (options == base64_options::base64_url_accept_garbage) {
1859
0
      return compress_decode_base64<true, true, false>(
1860
0
          output, input, length, options, last_chunk_options);
1861
0
    } else {
1862
0
      return compress_decode_base64<true, false, false>(
1863
0
          output, input, length, options, last_chunk_options);
1864
0
    }
1865
0
  } else {
1866
0
    if (options == base64_options::base64_default_accept_garbage) {
1867
0
      return compress_decode_base64<false, true, false>(
1868
0
          output, input, length, options, last_chunk_options);
1869
0
    } else {
1870
0
      return compress_decode_base64<false, false, false>(
1871
0
          output, input, length, options, last_chunk_options);
1872
0
    }
1873
0
  }
1874
0
}
1875
1876
size_t implementation::binary_to_base64(const char *input, size_t length,
1877
                                        char *output,
1878
0
                                        base64_options options) const noexcept {
1879
0
  if (options & base64_url) {
1880
0
    return encode_base64<true>(output, input, length, options);
1881
0
  } else {
1882
0
    return encode_base64<false>(output, input, length, options);
1883
0
  }
1884
0
}
1885
1886
size_t implementation::binary_to_base64_with_lines(
1887
    const char *input, size_t length, char *output, size_t line_length,
1888
0
    base64_options options) const noexcept {
1889
0
  if (options & base64_url) {
1890
0
    return encode_base64_impl<true, true>(output, input, length, options,
1891
0
                                          line_length);
1892
0
  } else {
1893
0
    return encode_base64_impl<false, true>(output, input, length, options,
1894
0
                                           line_length);
1895
0
  }
1896
0
}
1897
1898
const char *implementation::find(const char *start, const char *end,
1899
0
                                 char character) const noexcept {
1900
0
  return util_find(start, end, character);
1901
0
}
1902
const char16_t *implementation::find(const char16_t *start, const char16_t *end,
1903
0
                                     char16_t character) const noexcept {
1904
0
  return util_find(start, end, character);
1905
0
}
1906
1907
simdutf_warn_unused size_t implementation::binary_length_from_base64(
1908
0
    const char *input, size_t length) const noexcept {
1909
0
  return icelake_binary_length_from_base64(input, length);
1910
0
}
1911
1912
simdutf_warn_unused size_t implementation::binary_length_from_base64(
1913
0
    const char16_t *input, size_t length) const noexcept {
1914
0
  return icelake_binary_length_from_base64(input, length);
1915
0
}
1916
#endif // SIMDUTF_FEATURE_BASE64
1917
1918
} // namespace SIMDUTF_IMPLEMENTATION
1919
} // namespace simdutf
1920
1921
#include "simdutf/icelake/end.h"