Coverage Report

Created: 2026-09-01 06:49

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
  // Get the 512-bit reads onto a 64-byte boundary. A load whose address
195
  // straddles a cache line costs two accesses, and callers rarely hand us an
196
  // aligned buffer.
197
  //
198
  // We cannot simply mask-load a short head block to reach the boundary: the
199
  // checker carries state from one block to the next, and zero padding in the
200
  // middle of a character would read as a truncated sequence. Instead we
201
  // consume one full (unaligned) block and re-seed the cross-block state from
202
  // the three bytes preceding the aligned start. Those three bytes must lie
203
  // inside the buffer, hence the requirement that the adjustment be at least
204
  // three. Below a couple of kilobytes the fixed cost of the prologue is not
205
  // repaid.
206
0
  if (len >= 2048) {
207
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(ptr) % 64;
208
0
    if (misalignment != 0 && misalignment <= 61) {
209
0
      const size_t adjustment = 64 - misalignment;
210
0
      checker.check_next_input(_mm512_loadu_si512((const __m512i *)ptr));
211
0
      ptr += adjustment;
212
      // Only the top three lanes are read. Masked-out lanes never fault, so
213
      // this is safe even though ptr - 64 may point before buf.
214
0
      const __m512i prev3 = _mm512_maskz_loadu_epi8(
215
0
          UINT64_C(0xE000000000000000), (const __m512i *)(ptr - 64));
216
0
      checker.prev_input_block = prev3;
217
0
      checker.prev_incomplete = is_incomplete(prev3);
218
0
    }
219
0
  }
220
0
  for (; end - ptr >= 64; ptr += 64) {
221
0
    const __m512i utf8 = _mm512_loadu_si512((const __m512i *)ptr);
222
0
    checker.check_next_input(utf8);
223
0
  }
224
0
  if (end != ptr) {
225
0
    const __m512i utf8 = _mm512_maskz_loadu_epi8(
226
0
        ~UINT64_C(0) >> (64 - (end - ptr)), (const __m512i *)ptr);
227
0
    checker.check_next_input(utf8);
228
0
  }
229
0
  checker.check_eof();
230
0
  return !checker.errors();
231
0
}
232
#endif // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
233
234
#if SIMDUTF_FEATURE_UTF8
235
simdutf_warn_unused result implementation::validate_utf8_with_errors(
236
0
    const char *buf, size_t len) const noexcept {
237
0
  if (simdutf_unlikely(len == 0)) {
238
0
    return result(error_code::SUCCESS, len);
239
0
  }
240
0
  avx512_utf8_checker checker{};
241
0
  const char *ptr = buf;
242
0
  const char *end = ptr + len;
243
0
  size_t count{0};
244
  // Largest prefix that a clean error check has already cleared. On failure it
245
  // is handed to the scalar rewind, which re-validates forward from there to
246
  // the end of the buffer, so naming a position earlier than the error only
247
  // costs scalar work on the error path.
248
0
  size_t safe{0};
249
  // Get the 512-bit reads onto a 64-byte boundary. A load whose address is not
250
  // aligned touches two cache lines and costs two accesses, and callers rarely
251
  // hand us an aligned buffer.
252
  //
253
  // The head has to be a full block rather than a masked one: the checker
254
  // carries state from one block to the next, and zero padding in the middle
255
  // of a character would read as a truncated sequence. The cross-block state
256
  // is then re-seeded from the three bytes preceding the aligned start, which
257
  // must be inside the buffer, hence the misalignment <= 61 guard.
258
0
  if (len >= 2048) {
259
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(ptr) % 64;
260
0
    if (misalignment != 0 && misalignment <= 61) {
261
0
      const size_t adjustment = 64 - misalignment;
262
0
      checker.check_next_input(_mm512_loadu_si512((const __m512i *)ptr));
263
0
      if (simdutf_unlikely(checker.errors())) {
264
0
        return scalar::utf8::rewind_and_validate_with_errors(buf, buf, len);
265
0
      }
266
0
      ptr += adjustment;
267
0
      count = adjustment;
268
      // Only the top three lanes are read. Masked-out lanes never fault, so
269
      // this is safe even though ptr - 64 may point before buf.
270
0
      const __m512i prev3 = _mm512_maskz_loadu_epi8(
271
0
          UINT64_C(0xE000000000000000), (const __m512i *)(ptr - 64));
272
0
      checker.prev_input_block = prev3;
273
0
      checker.prev_incomplete = is_incomplete(prev3);
274
0
    }
275
0
  }
276
  // checker.error is a sticky OR-accumulator, so it does not have to be tested
277
  // every 64 bytes. Testing it every eighth block takes a vptestmb, a ktest
278
  // and a branch out of the hot loop; an error is then handed to the scalar
279
  // rewind at most nine blocks early, which only lengthens the rare error
280
  // path.
281
0
  unsigned since = 0;
282
0
  for (; end - ptr >= 64; ptr += 64) {
283
0
    const __m512i utf8 = _mm512_loadu_si512((const __m512i *)ptr);
284
0
    checker.check_next_input(utf8);
285
0
    count += 64;
286
0
    if (++since == 8) {
287
0
      since = 0;
288
0
      if (simdutf_unlikely(checker.errors())) {
289
0
        break;
290
0
      }
291
0
      safe = count >= 64 ? count - 64 : 0;
292
0
    }
293
0
  }
294
0
  if (!checker.errors() && end != ptr) {
295
0
    const __m512i utf8 = _mm512_maskz_loadu_epi8(
296
0
        ~UINT64_C(0) >> (64 - (end - ptr)), (const __m512i *)ptr);
297
0
    checker.check_next_input(utf8);
298
0
  }
299
0
  checker.check_eof();
300
0
  if (checker.errors()) {
301
0
    if (safe != 0) {
302
0
      safe--;
303
0
    } // Sometimes the error is only detected in the next chunk
304
0
    result res = scalar::utf8::rewind_and_validate_with_errors(
305
0
        reinterpret_cast<const char *>(buf),
306
0
        reinterpret_cast<const char *>(buf + safe), len - safe);
307
0
    res.count += safe;
308
0
    return res;
309
0
  }
310
0
  return result(error_code::SUCCESS, len);
311
0
}
312
#endif // SIMDUTF_FEATURE_UTF8
313
314
#if SIMDUTF_FEATURE_ASCII
315
simdutf_warn_unused bool
316
0
implementation::validate_ascii(const char *buf, size_t len) const noexcept {
317
0
  return icelake::validate_ascii(buf, len);
318
0
}
319
320
simdutf_warn_unused result implementation::validate_ascii_with_errors(
321
0
    const char *buf, size_t len) const noexcept {
322
0
  const char *buf_orig = buf;
323
0
  const char *end = buf + len;
324
0
  const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80);
325
  // Reach the next 64-byte boundary first: a 512-bit load whose address
326
  // straddles a cache line costs two accesses, and this loop does nothing but
327
  // load and compare. There is no cross-block state, so the head is simply a
328
  // shorter first block handled with a masked load.
329
0
  if (len >= 64) {
330
    // A full first block, exactly as before, so that inputs whose first
331
    // non-ASCII byte is near the start still return just as quickly. Once it
332
    // is known to be ASCII we may jump to the boundary; re-reading the bytes
333
    // in between is harmless because there is no cross-block state.
334
0
    const __m512i head = _mm512_loadu_si512((const __m512i *)buf);
335
0
    __mmask64 notascii = _mm512_cmp_epu8_mask(head, ascii, _MM_CMPINT_NLT);
336
0
    if (notascii) {
337
0
      return result(error_code::TOO_LARGE,
338
0
                    buf - buf_orig + _tzcnt_u64(notascii));
339
0
    }
340
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
341
0
    buf += (misalignment == 0) ? 64 : (64 - misalignment);
342
0
  }
343
  // Four vectors per compare-and-branch: the loads then issue back to back
344
  // instead of being serialized by one branch per 64 bytes.
345
0
  for (; end - buf >= 256; buf += 256) {
346
0
    const __m512i b0 = _mm512_loadu_si512((const __m512i *)buf);
347
0
    const __m512i b1 = _mm512_loadu_si512((const __m512i *)(buf + 64));
348
0
    const __m512i b2 = _mm512_loadu_si512((const __m512i *)(buf + 128));
349
0
    const __m512i b3 = _mm512_loadu_si512((const __m512i *)(buf + 192));
350
0
    const __m512i any =
351
0
        _mm512_or_si512(_mm512_or_si512(b0, b1), _mm512_or_si512(b2, b3));
352
0
    if (_mm512_cmp_epu8_mask(any, ascii, _MM_CMPINT_NLT)) {
353
0
      break; // the 64-byte loop below pinpoints it
354
0
    }
355
0
  }
356
0
  for (; end - buf >= 64; buf += 64) {
357
0
    const __m512i input = _mm512_loadu_si512((const __m512i *)buf);
358
0
    __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
359
0
    if (notascii) {
360
0
      return result(error_code::TOO_LARGE,
361
0
                    buf - buf_orig + _tzcnt_u64(notascii));
362
0
    }
363
0
  }
364
0
  if (end != buf) {
365
0
    const __m512i input = _mm512_maskz_loadu_epi8(
366
0
        ~UINT64_C(0) >> (64 - (end - buf)), (const __m512i *)buf);
367
0
    __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
368
0
    if (notascii) {
369
0
      return result(error_code::TOO_LARGE,
370
0
                    buf - buf_orig + _tzcnt_u64(notascii));
371
0
    }
372
0
  }
373
0
  return result(error_code::SUCCESS, len);
374
0
}
375
#endif // SIMDUTF_FEATURE_ASCII
376
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
377
simdutf_warn_unused bool
378
implementation::validate_utf16le_as_ascii(const char16_t *buf,
379
0
                                          size_t len) const noexcept {
380
0
  const char16_t *end = buf + len;
381
0
  __m512i limit = _mm512_set1_epi16(uint16_t(0x007F));
382
  // Reach a 64-byte boundary; a 512-bit load that straddles a cache line costs
383
  // two accesses. Nothing crosses a block boundary here, so the head is simply
384
  // a shorter first block, and the zero fill of a masked load is itself ASCII.
385
0
  if (len >= 32) {
386
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
387
0
    if (misalignment != 0) {
388
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
389
0
      const __m512i head = _mm512_maskz_loadu_epi16(
390
0
          __mmask32((1U << adjustment) - 1), (const __m512i *)buf);
391
0
      if (_mm512_cmpgt_epu16_mask(head, limit)) {
392
0
        return false;
393
0
      }
394
0
      buf += adjustment;
395
0
    }
396
0
  }
397
0
  for (; end - buf >= 32;) {
398
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
399
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
400
0
    if (mask) {
401
0
      return false;
402
0
    }
403
0
    buf += 32;
404
0
  }
405
0
  if (buf < end) {
406
0
    __m512i in =
407
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
408
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
409
0
    if (mask) {
410
0
      return false;
411
0
    }
412
0
  }
413
0
  return true;
414
0
}
415
416
simdutf_warn_unused bool
417
implementation::validate_utf16be_as_ascii(const char16_t *buf,
418
0
                                          size_t len) const noexcept {
419
0
  const char16_t *end = buf + len;
420
0
  const __m512i byteflip = _mm512_setr_epi64(
421
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
422
0
      0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
423
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809);
424
0
  __m512i limit = _mm512_set1_epi16(uint16_t(0x007F));
425
  // Reach a 64-byte boundary; a 512-bit load that straddles a cache line costs
426
  // two accesses. Nothing crosses a block boundary here, so the head is simply
427
  // a shorter first block, and the zero fill of a masked load is itself ASCII.
428
0
  if (len >= 32) {
429
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
430
0
    if (misalignment != 0) {
431
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
432
0
      const __m512i head = _mm512_shuffle_epi8(
433
0
          _mm512_maskz_loadu_epi16(__mmask32((1U << adjustment) - 1),
434
0
                                   (const __m512i *)buf),
435
0
          byteflip);
436
0
      if (_mm512_cmpgt_epu16_mask(head, limit)) {
437
0
        return false;
438
0
      }
439
0
      buf += adjustment;
440
0
    }
441
0
  }
442
0
  for (; end - buf >= 32;) {
443
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
444
0
    in = _mm512_shuffle_epi8(in, byteflip);
445
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
446
0
    if (mask) {
447
0
      return false;
448
0
    }
449
0
    buf += 32;
450
0
  }
451
0
  if (buf < end) {
452
0
    __m512i in =
453
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
454
0
    in = _mm512_shuffle_epi8(in, byteflip);
455
0
    auto mask = _mm512_cmpgt_epu16_mask(in, limit);
456
0
    if (mask) {
457
0
      return false;
458
0
    }
459
0
  }
460
0
  return true;
461
0
}
462
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
463
#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
464
simdutf_warn_unused bool
465
implementation::validate_utf16le(const char16_t *buf,
466
0
                                 size_t len) const noexcept {
467
0
  const char16_t *end = buf + len;
468
469
  // Optimized: Process 64 code units (2x 512-bit) per iteration
470
0
  const __m512i surr_base = _mm512_set1_epi16(uint16_t(0xD800));
471
0
  const __m512i surr_range = _mm512_set1_epi16(uint16_t(0x0800));
472
0
  const __m512i high_range = _mm512_set1_epi16(uint16_t(0x0400));
473
474
  // Reach a 64-byte boundary: a 512-bit load whose address straddles a cache
475
  // line costs two accesses, and this loop is load-bound. The only state that
476
  // crosses a block boundary here is a surrogate pair, so we may skip over the
477
  // head only when it holds no surrogate at all -- which also makes the head
478
  // valid, so nothing else about it needs checking. Input whose first code
479
  // units are surrogates simply keeps to the unaligned path.
480
0
  if (len >= 32) {
481
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
482
0
    if (misalignment != 0) {
483
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
484
0
      const __m512i head = _mm512_maskz_loadu_epi16(
485
0
          __mmask32((1U << adjustment) - 1), (const __m512i *)buf);
486
0
      const __m512i headdiff =
487
0
          _mm512_sub_epi16(head, _mm512_set1_epi16(uint16_t(0xD800)));
488
0
      if (_mm512_cmplt_epu16_mask(headdiff,
489
0
                                  _mm512_set1_epi16(uint16_t(0x0800))) == 0) {
490
0
        buf += adjustment;
491
0
      }
492
0
    }
493
0
  }
494
0
  for (; end - buf >= 64;) {
495
0
    __m512i in_1 = _mm512_loadu_si512((__m512i *)buf);
496
0
    __m512i in_2 = _mm512_loadu_si512((__m512i *)(buf + 32));
497
498
0
    __m512i diff_1 = _mm512_sub_epi16(in_1, surr_base);
499
0
    __m512i diff_2 = _mm512_sub_epi16(in_2, surr_base);
500
501
0
    __mmask32 surrogates_1 = _mm512_cmplt_epu16_mask(diff_1, surr_range);
502
0
    __mmask32 surrogates_2 = _mm512_cmplt_epu16_mask(diff_2, surr_range);
503
504
0
    if (surrogates_1 | surrogates_2) {
505
0
      __mmask32 highsurrogates_1 = _mm512_cmplt_epu16_mask(diff_1, high_range);
506
0
      __mmask32 lowsurrogates_1 = surrogates_1 ^ highsurrogates_1;
507
508
0
      __mmask32 highsurrogates_2 = _mm512_cmplt_epu16_mask(diff_2, high_range);
509
0
      __mmask32 lowsurrogates_2 = surrogates_2 ^ highsurrogates_2;
510
511
      // Validate first block: high must be followed by low
512
0
      if ((highsurrogates_1 << 1) != lowsurrogates_1) {
513
0
        return false;
514
0
      }
515
516
      // Check boundary between blocks: if first block ends with high, second
517
      // must start with low
518
0
      bool ends_with_high_1 = ((highsurrogates_1 & 0x80000000) != 0);
519
0
      bool starts_with_low_2 = ((lowsurrogates_2 & 0x1) != 0);
520
0
      if (ends_with_high_1 && !starts_with_low_2) {
521
0
        return false;
522
0
      }
523
524
      // Validate second block (shift by 1 if first ended with high)
525
0
      __mmask32 expected_low_2 = ends_with_high_1
526
0
                                     ? (highsurrogates_2 << 1) | 0x1
527
0
                                     : (highsurrogates_2 << 1);
528
0
      if (expected_low_2 != lowsurrogates_2) {
529
0
        return false;
530
0
      }
531
532
0
      bool ends_with_high_2 = ((highsurrogates_2 & 0x80000000) != 0);
533
0
      if (ends_with_high_2) {
534
0
        buf += 63; // advance by 63 to start with high surrogate next round
535
0
      } else {
536
0
        buf += 64;
537
0
      }
538
0
    } else {
539
0
      buf += 64;
540
0
    }
541
0
  }
542
543
  // Handle remaining 32-63 code units
544
0
  for (; end - buf >= 32;) {
545
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
546
0
    __m512i diff = _mm512_sub_epi16(in, surr_base);
547
0
    __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, surr_range);
548
0
    if (surrogates) {
549
0
      __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, high_range);
550
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
551
      // high must be followed by low
552
0
      if ((highsurrogates << 1) != lowsurrogates) {
553
0
        return false;
554
0
      }
555
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
556
0
      if (ends_with_high) {
557
0
        buf += 31; // advance only by 31 code units so that we start with the
558
                   // high surrogate on the next round.
559
0
      } else {
560
0
        buf += 32;
561
0
      }
562
0
    } else {
563
0
      buf += 32;
564
0
    }
565
0
  }
566
0
  if (buf < end) {
567
0
    __m512i in =
568
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
569
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
570
0
    __mmask32 surrogates =
571
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
572
0
    if (surrogates) {
573
0
      __mmask32 highsurrogates =
574
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
575
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
576
      // high must be followed by low
577
0
      if ((highsurrogates << 1) != lowsurrogates) {
578
0
        return false;
579
0
      }
580
0
    }
581
0
  }
582
0
  return true;
583
0
}
584
#endif // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
585
586
#if SIMDUTF_FEATURE_UTF16
587
simdutf_warn_unused bool
588
implementation::validate_utf16be(const char16_t *buf,
589
0
                                 size_t len) const noexcept {
590
0
  const char16_t *end = buf + len;
591
592
  // Reach a 64-byte boundary: a 512-bit load whose address straddles a cache
593
  // line costs two accesses, and this loop is load-bound. The only state that
594
  // crosses a block boundary here is a surrogate pair, so we may skip over the
595
  // head only when it holds no surrogate at all -- which also makes the head
596
  // valid, so nothing else about it needs checking. Input whose first code
597
  // units are surrogates simply keeps to the unaligned path.
598
0
  if (len >= 32) {
599
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
600
0
    if (misalignment != 0) {
601
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
602
0
      const __m512i head = _mm512_slli_epi16(
603
0
          _mm512_maskz_loadu_epi16(__mmask32((1U << adjustment) - 1),
604
0
                                   (const __m512i *)buf),
605
0
          8);
606
0
      const __m512i headdiff =
607
0
          _mm512_sub_epi16(head, _mm512_set1_epi16(uint16_t(0xD800)));
608
0
      if (_mm512_cmplt_epu16_mask(headdiff,
609
0
                                  _mm512_set1_epi16(uint16_t(0x0800))) == 0) {
610
0
        buf += adjustment;
611
0
      }
612
0
    }
613
0
  }
614
0
  for (; end - buf >= 32;) {
615
0
    __m512i in = _mm512_slli_epi32(_mm512_loadu_si512((__m512i *)buf), 8);
616
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
617
0
    __mmask32 surrogates =
618
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
619
0
    if (surrogates) {
620
0
      __mmask32 highsurrogates =
621
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
622
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
623
      // high must be followed by low
624
0
      if ((highsurrogates << 1) != lowsurrogates) {
625
0
        return false;
626
0
      }
627
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
628
0
      if (ends_with_high) {
629
0
        buf += 31; // advance only by 31 code units so that we start with the
630
                   // high surrogate on the next round.
631
0
      } else {
632
0
        buf += 32;
633
0
      }
634
0
    } else {
635
0
      buf += 32;
636
0
    }
637
0
  }
638
0
  if (buf < end) {
639
0
    __m512i in = _mm512_slli_epi16(
640
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf), 8);
641
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
642
0
    __mmask32 surrogates =
643
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
644
0
    if (surrogates) {
645
0
      __mmask32 highsurrogates =
646
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
647
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
648
      // high must be followed by low
649
0
      if ((highsurrogates << 1) != lowsurrogates) {
650
0
        return false;
651
0
      }
652
0
    }
653
0
  }
654
0
  return true;
655
0
}
656
657
simdutf_warn_unused result implementation::validate_utf16le_with_errors(
658
0
    const char16_t *buf, size_t len) const noexcept {
659
0
  const char16_t *start_buf = buf;
660
0
  const char16_t *end = buf + len;
661
  // Reach a 64-byte boundary: a 512-bit load whose address straddles a cache
662
  // line costs two accesses, and this loop is load-bound. The only state that
663
  // crosses a block boundary here is a surrogate pair, so we may skip over the
664
  // head only when it holds no surrogate at all -- which also makes the head
665
  // valid, so nothing else about it needs checking. Input whose first code
666
  // units are surrogates simply keeps to the unaligned path.
667
0
  if (len >= 32) {
668
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
669
0
    if (misalignment != 0) {
670
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
671
0
      const __m512i head = _mm512_maskz_loadu_epi16(
672
0
          __mmask32((1U << adjustment) - 1), (const __m512i *)buf);
673
0
      const __m512i headdiff =
674
0
          _mm512_sub_epi16(head, _mm512_set1_epi16(uint16_t(0xD800)));
675
0
      if (_mm512_cmplt_epu16_mask(headdiff,
676
0
                                  _mm512_set1_epi16(uint16_t(0x0800))) == 0) {
677
0
        buf += adjustment;
678
0
      }
679
0
    }
680
0
  }
681
0
  for (; end - buf >= 32;) {
682
0
    __m512i in = _mm512_loadu_si512((__m512i *)buf);
683
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
684
0
    __mmask32 surrogates =
685
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
686
0
    if (surrogates) {
687
0
      __mmask32 highsurrogates =
688
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
689
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
690
      // high must be followed by low
691
0
      if ((highsurrogates << 1) != lowsurrogates) {
692
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
693
0
        uint32_t extra_high =
694
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
695
0
        return result(error_code::SURROGATE,
696
0
                      (buf - start_buf) +
697
0
                          (extra_low < extra_high ? extra_low : extra_high));
698
0
      }
699
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
700
0
      if (ends_with_high) {
701
0
        buf += 31; // advance only by 31 code units so that we start with the
702
                   // high surrogate on the next round.
703
0
      } else {
704
0
        buf += 32;
705
0
      }
706
0
    } else {
707
0
      buf += 32;
708
0
    }
709
0
  }
710
0
  if (buf < end) {
711
0
    __m512i in =
712
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf);
713
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
714
0
    __mmask32 surrogates =
715
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
716
0
    if (surrogates) {
717
0
      __mmask32 highsurrogates =
718
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
719
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
720
      // high must be followed by low
721
0
      if ((highsurrogates << 1) != lowsurrogates) {
722
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
723
0
        uint32_t extra_high =
724
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
725
0
        return result(error_code::SURROGATE,
726
0
                      (buf - start_buf) +
727
0
                          (extra_low < extra_high ? extra_low : extra_high));
728
0
      }
729
0
    }
730
0
  }
731
0
  return result(error_code::SUCCESS, len);
732
0
}
733
734
simdutf_warn_unused result implementation::validate_utf16be_with_errors(
735
0
    const char16_t *buf, size_t len) const noexcept {
736
0
  const char16_t *start_buf = buf;
737
0
  const char16_t *end = buf + len;
738
739
  // Reach a 64-byte boundary: a 512-bit load whose address straddles a cache
740
  // line costs two accesses, and this loop is load-bound. The only state that
741
  // crosses a block boundary here is a surrogate pair, so we may skip over the
742
  // head only when it holds no surrogate at all -- which also makes the head
743
  // valid, so nothing else about it needs checking. Input whose first code
744
  // units are surrogates simply keeps to the unaligned path.
745
0
  if (len >= 32) {
746
0
    const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
747
0
    if (misalignment != 0) {
748
0
      const size_t adjustment = (64 - misalignment) / sizeof(char16_t);
749
0
      const __m512i head = _mm512_slli_epi16(
750
0
          _mm512_maskz_loadu_epi16(__mmask32((1U << adjustment) - 1),
751
0
                                   (const __m512i *)buf),
752
0
          8);
753
0
      const __m512i headdiff =
754
0
          _mm512_sub_epi16(head, _mm512_set1_epi16(uint16_t(0xD800)));
755
0
      if (_mm512_cmplt_epu16_mask(headdiff,
756
0
                                  _mm512_set1_epi16(uint16_t(0x0800))) == 0) {
757
0
        buf += adjustment;
758
0
      }
759
0
    }
760
0
  }
761
0
  for (; end - buf >= 32;) {
762
0
    __m512i in = _mm512_slli_epi16(_mm512_loadu_si512((__m512i *)buf), 8);
763
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
764
0
    __mmask32 surrogates =
765
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
766
0
    if (surrogates) {
767
0
      __mmask32 highsurrogates =
768
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
769
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
770
      // high must be followed by low
771
0
      if ((highsurrogates << 1) != lowsurrogates) {
772
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
773
0
        uint32_t extra_high =
774
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
775
0
        return result(error_code::SURROGATE,
776
0
                      (buf - start_buf) +
777
0
                          (extra_low < extra_high ? extra_low : extra_high));
778
0
      }
779
0
      bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
780
0
      if (ends_with_high) {
781
0
        buf += 31; // advance only by 31 code units so that we start with the
782
                   // high surrogate on the next round.
783
0
      } else {
784
0
        buf += 32;
785
0
      }
786
0
    } else {
787
0
      buf += 32;
788
0
    }
789
0
  }
790
0
  if (buf < end) {
791
0
    __m512i in = _mm512_slli_epi16(
792
0
        _mm512_maskz_loadu_epi16((1U << (end - buf)) - 1, (__m512i *)buf), 8);
793
0
    __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
794
0
    __mmask32 surrogates =
795
0
        _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
796
0
    if (surrogates) {
797
0
      __mmask32 highsurrogates =
798
0
          _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
799
0
      __mmask32 lowsurrogates = surrogates ^ highsurrogates;
800
      // high must be followed by low
801
0
      if ((highsurrogates << 1) != lowsurrogates) {
802
0
        uint32_t extra_low = _tzcnt_u32(lowsurrogates & ~(highsurrogates << 1));
803
0
        uint32_t extra_high =
804
0
            _tzcnt_u32(highsurrogates & ~(lowsurrogates >> 1));
805
0
        return result(error_code::SURROGATE,
806
0
                      (buf - start_buf) +
807
0
                          (extra_low < extra_high ? extra_low : extra_high));
808
0
      }
809
0
    }
810
0
  }
811
0
  return result(error_code::SUCCESS, len);
812
0
}
813
814
void implementation::to_well_formed_utf16le(const char16_t *input, size_t len,
815
0
                                            char16_t *output) const noexcept {
816
0
  return utf16fix_avx512<endianness::LITTLE>(input, len, output);
817
0
}
818
819
void implementation::to_well_formed_utf16be(const char16_t *input, size_t len,
820
0
                                            char16_t *output) const noexcept {
821
0
  return utf16fix_avx512<endianness::BIG>(input, len, output);
822
0
}
823
#endif // SIMDUTF_FEATURE_UTF16
824
825
#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
826
simdutf_warn_unused bool
827
0
implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
828
0
  return icelake::validate_utf32(buf, len);
829
0
}
830
#endif // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
831
832
#if SIMDUTF_FEATURE_UTF32
833
simdutf_warn_unused result implementation::validate_utf32_with_errors(
834
0
    const char32_t *buf, size_t len) const noexcept {
835
0
  const char32_t *buf_orig = buf;
836
0
  if (len >= 16) {
837
0
    const char32_t *end = buf + len - 16;
838
    // One full block first, exactly as before, so that inputs whose first bad
839
    // code point is near the start still return just as quickly. Once it is
840
    // known to be clean we may jump to the 64-byte boundary; re-reading the
841
    // values in between is harmless because no state crosses blocks.
842
0
    {
843
0
      __m512i utf32 = _mm512_loadu_si512((const __m512i *)buf);
844
0
      __mmask16 outside_range = _mm512_cmp_epu32_mask(
845
0
          utf32, _mm512_set1_epi32(0x10ffff), _MM_CMPINT_GT);
846
0
      __m512i utf32_off =
847
0
          _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
848
0
      __mmask16 surrogate_range = _mm512_cmp_epu32_mask(
849
0
          utf32_off, _mm512_set1_epi32(0xfffff7ff), _MM_CMPINT_GT);
850
0
      if ((outside_range | surrogate_range)) {
851
0
        auto outside_idx = _tzcnt_u32(outside_range);
852
0
        auto surrogate_idx = _tzcnt_u32(surrogate_range);
853
0
        if (outside_idx < surrogate_idx) {
854
0
          return result(error_code::TOO_LARGE, buf - buf_orig + outside_idx);
855
0
        }
856
0
        return result(error_code::SURROGATE, buf - buf_orig + surrogate_idx);
857
0
      }
858
0
      const uintptr_t misalignment = reinterpret_cast<uintptr_t>(buf) % 64;
859
0
      buf += (misalignment == 0) ? 16 : (64 - misalignment) / sizeof(char32_t);
860
0
    }
861
    // Screen four vectors per compare-and-branch; the 16-value loop below
862
    // pinpoints the offending code point.
863
0
    const __m512i toolarge = _mm512_set1_epi32(0x10ffff);
864
0
    const __m512i offset = _mm512_set1_epi32(0xffff2000);
865
0
    const __m512i surrmax = _mm512_set1_epi32(0xfffff7ff);
866
0
    while (buf + 48 <= end) {
867
0
      __m512i a = _mm512_loadu_si512((const __m512i *)buf);
868
0
      __m512i b = _mm512_loadu_si512((const __m512i *)(buf + 16));
869
0
      __m512i c = _mm512_loadu_si512((const __m512i *)(buf + 32));
870
0
      __m512i d = _mm512_loadu_si512((const __m512i *)(buf + 48));
871
0
      __m512i mx =
872
0
          _mm512_max_epu32(_mm512_max_epu32(a, b), _mm512_max_epu32(c, d));
873
0
      __m512i ox =
874
0
          _mm512_max_epu32(_mm512_max_epu32(_mm512_add_epi32(a, offset),
875
0
                                            _mm512_add_epi32(b, offset)),
876
0
                           _mm512_max_epu32(_mm512_add_epi32(c, offset),
877
0
                                            _mm512_add_epi32(d, offset)));
878
0
      if (_mm512_cmp_epu32_mask(mx, toolarge, _MM_CMPINT_GT) |
879
0
          _mm512_cmp_epu32_mask(ox, surrmax, _MM_CMPINT_GT)) {
880
0
        break;
881
0
      }
882
0
      buf += 64;
883
0
    }
884
0
    while (buf <= end) {
885
0
      __m512i utf32 = _mm512_loadu_si512((const __m512i *)buf);
886
0
      __mmask16 outside_range = _mm512_cmp_epu32_mask(
887
0
          utf32, _mm512_set1_epi32(0x10ffff), _MM_CMPINT_GT);
888
889
0
      __m512i utf32_off =
890
0
          _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
891
892
0
      __mmask16 surrogate_range = _mm512_cmp_epu32_mask(
893
0
          utf32_off, _mm512_set1_epi32(0xfffff7ff), _MM_CMPINT_GT);
894
0
      if ((outside_range | surrogate_range)) {
895
0
        auto outside_idx = _tzcnt_u32(outside_range);
896
0
        auto surrogate_idx = _tzcnt_u32(surrogate_range);
897
898
0
        if (outside_idx < surrogate_idx) {
899
0
          return result(error_code::TOO_LARGE, buf - buf_orig + outside_idx);
900
0
        }
901
902
0
        return result(error_code::SURROGATE, buf - buf_orig + surrogate_idx);
903
0
      }
904
905
0
      buf += 16;
906
0
    }
907
0
  }
908
0
  if (len > 0) {
909
0
    __m512i utf32 = _mm512_maskz_loadu_epi32(
910
0
        __mmask16((1U << (buf_orig + len - buf)) - 1), (const __m512i *)buf);
911
0
    __mmask16 outside_range = _mm512_cmp_epu32_mask(
912
0
        utf32, _mm512_set1_epi32(0x10ffff), _MM_CMPINT_GT);
913
0
    __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
914
915
0
    __mmask16 surrogate_range = _mm512_cmp_epu32_mask(
916
0
        utf32_off, _mm512_set1_epi32(0xfffff7ff), _MM_CMPINT_GT);
917
0
    if ((outside_range | surrogate_range)) {
918
0
      auto outside_idx = _tzcnt_u32(outside_range);
919
0
      auto surrogate_idx = _tzcnt_u32(surrogate_range);
920
921
0
      if (outside_idx < surrogate_idx) {
922
0
        return result(error_code::TOO_LARGE, buf - buf_orig + outside_idx);
923
0
      }
924
925
0
      return result(error_code::SURROGATE, buf - buf_orig + surrogate_idx);
926
0
    }
927
0
  }
928
929
0
  return result(error_code::SUCCESS, len);
930
0
}
931
#endif // SIMDUTF_FEATURE_UTF32
932
933
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
934
simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(
935
0
    const char *buf, size_t len, char *utf8_output) const noexcept {
936
0
  return icelake::latin1_to_utf8_avx512_start(buf, len, utf8_output);
937
0
}
938
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
939
940
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
941
simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(
942
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
943
0
  return icelake_convert_latin1_to_utf16<endianness::LITTLE>(buf, len,
944
0
                                                             utf16_output);
945
0
}
946
947
simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(
948
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
949
0
  return icelake_convert_latin1_to_utf16<endianness::BIG>(buf, len,
950
0
                                                          utf16_output);
951
0
}
952
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
953
954
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
955
simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(
956
0
    const char *buf, size_t len, char32_t *utf32_output) const noexcept {
957
0
  avx512_convert_latin1_to_utf32(buf, len, utf32_output);
958
0
  return len;
959
0
}
960
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
961
962
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
963
simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(
964
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
965
0
  return icelake::utf8_to_latin1_avx512(buf, len, latin1_output);
966
0
}
967
968
simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(
969
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
970
  // First, try to convert as much as possible using the SIMD implementation.
971
0
  const char *obuf = buf;
972
0
  char *olatin1_output = latin1_output;
973
0
  size_t written = icelake::utf8_to_latin1_avx512(obuf, len, olatin1_output);
974
975
  // If we have completely converted the string
976
0
  if (obuf == buf + len) {
977
0
    return {simdutf::SUCCESS, written};
978
0
  }
979
0
  size_t pos = obuf - buf;
980
0
  result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(
981
0
      pos, buf + pos, len - pos, latin1_output);
982
0
  res.count += pos;
983
0
  return res;
984
0
}
985
986
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(
987
0
    const char *buf, size_t len, char *latin1_output) const noexcept {
988
0
  return icelake::valid_utf8_to_latin1_avx512(buf, len, latin1_output);
989
0
}
990
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
991
992
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
993
simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(
994
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
995
0
  utf8_to_utf16_result ret =
996
0
      fast_avx512_convert_utf8_to_utf16<endianness::LITTLE>(buf, len,
997
0
                                                            utf16_output);
998
0
  if (ret.second == nullptr) {
999
0
    return 0;
1000
0
  }
1001
0
  return ret.second - utf16_output;
1002
0
}
1003
1004
simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(
1005
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
1006
0
  utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16<endianness::BIG>(
1007
0
      buf, len, utf16_output);
1008
0
  if (ret.second == nullptr) {
1009
0
    return 0;
1010
0
  }
1011
0
  return ret.second - utf16_output;
1012
0
}
1013
1014
simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(
1015
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
1016
0
  return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::LITTLE>(
1017
0
      buf, len, utf16_output);
1018
0
}
1019
1020
simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(
1021
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
1022
0
  return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::BIG>(
1023
0
      buf, len, utf16_output);
1024
0
}
1025
1026
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(
1027
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
1028
0
  utf8_to_utf16_result ret =
1029
0
      icelake::valid_utf8_to_fixed_length<endianness::LITTLE, char16_t>(
1030
0
          buf, len, utf16_output);
1031
0
  size_t saved_bytes = ret.second - utf16_output;
1032
0
  const char *end = buf + len;
1033
0
  if (ret.first == end) {
1034
0
    return saved_bytes;
1035
0
  }
1036
1037
  // Note: AVX512 procedure looks up 4 bytes forward, and
1038
  //       correctly converts multi-byte chars even if their
1039
  //       continuation bytes lie outsiede 16-byte window.
1040
  //       It meas, we have to skip continuation bytes from
1041
  //       the beginning ret.first, as they were already consumed.
1042
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
1043
0
    ret.first += 1;
1044
0
  }
1045
1046
0
  if (ret.first != end) {
1047
0
    const size_t scalar_saved_bytes =
1048
0
        scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(
1049
0
            ret.first, len - (ret.first - buf), ret.second);
1050
0
    if (scalar_saved_bytes == 0) {
1051
0
      return 0;
1052
0
    }
1053
0
    saved_bytes += scalar_saved_bytes;
1054
0
  }
1055
1056
0
  return saved_bytes;
1057
0
}
1058
1059
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(
1060
0
    const char *buf, size_t len, char16_t *utf16_output) const noexcept {
1061
0
  utf8_to_utf16_result ret =
1062
0
      icelake::valid_utf8_to_fixed_length<endianness::BIG, char16_t>(
1063
0
          buf, len, utf16_output);
1064
0
  size_t saved_bytes = ret.second - utf16_output;
1065
0
  const char *end = buf + len;
1066
0
  if (ret.first == end) {
1067
0
    return saved_bytes;
1068
0
  }
1069
1070
  // Note: AVX512 procedure looks up 4 bytes forward, and
1071
  //       correctly converts multi-byte chars even if their
1072
  //       continuation bytes lie outsiede 16-byte window.
1073
  //       It meas, we have to skip continuation bytes from
1074
  //       the beginning ret.first, as they were already consumed.
1075
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
1076
0
    ret.first += 1;
1077
0
  }
1078
1079
0
  if (ret.first != end) {
1080
0
    const size_t scalar_saved_bytes =
1081
0
        scalar::utf8_to_utf16::convert_valid<endianness::BIG>(
1082
0
            ret.first, len - (ret.first - buf), ret.second);
1083
0
    if (scalar_saved_bytes == 0) {
1084
0
      return 0;
1085
0
    }
1086
0
    saved_bytes += scalar_saved_bytes;
1087
0
  }
1088
1089
0
  return saved_bytes;
1090
0
}
1091
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1092
1093
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1094
simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(
1095
0
    const char *buf, size_t len, char32_t *utf32_out) const noexcept {
1096
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
1097
0
  utf8_to_utf32_result ret =
1098
0
      icelake::validating_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(
1099
0
          buf, len, utf32_output);
1100
0
  if (ret.second == nullptr)
1101
0
    return 0;
1102
1103
0
  size_t saved_bytes = ret.second - utf32_output;
1104
0
  const char *end = buf + len;
1105
0
  if (ret.first == end) {
1106
0
    return saved_bytes;
1107
0
  }
1108
1109
  // Note: the AVX512 procedure looks up 4 bytes forward, and
1110
  //       correctly converts multi-byte chars even if their
1111
  //       continuation bytes lie outside 16-byte window.
1112
  //       It means, we have to skip continuation bytes from
1113
  //       the beginning ret.first, as they were already consumed.
1114
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
1115
0
    ret.first += 1;
1116
0
  }
1117
0
  if (ret.first != end) {
1118
0
    const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert(
1119
0
        ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
1120
0
    if (scalar_saved_bytes == 0) {
1121
0
      return 0;
1122
0
    }
1123
0
    saved_bytes += scalar_saved_bytes;
1124
0
  }
1125
1126
0
  return saved_bytes;
1127
0
}
1128
1129
simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(
1130
0
    const char *buf, size_t len, char32_t *utf32) const noexcept {
1131
0
  if (simdutf_unlikely(len == 0)) {
1132
0
    return {error_code::SUCCESS, 0};
1133
0
  }
1134
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32);
1135
0
  auto ret = icelake::validating_utf8_to_fixed_length_with_constant_checks<
1136
0
      endianness::LITTLE, uint32_t>(buf, len, utf32_output);
1137
1138
0
  if (!std::get<2>(ret)) {
1139
0
    size_t pos = std::get<0>(ret) - buf;
1140
    // We might have an error that occurs right before  pos.
1141
    // This is only a concern if buf[pos] is not a continuation byte.
1142
0
    if ((buf[pos] & 0xc0) != 0x80 && pos >= 64) {
1143
0
      pos -= 1;
1144
0
    } else if ((buf[pos] & 0xc0) == 0x80 && pos >= 64) {
1145
      // We must check whether we are the fourth continuation byte
1146
0
      bool c1 = (buf[pos - 1] & 0xc0) == 0x80;
1147
0
      bool c2 = (buf[pos - 2] & 0xc0) == 0x80;
1148
0
      bool c3 = (buf[pos - 3] & 0xc0) == 0x80;
1149
0
      if (c1 && c2 && c3) {
1150
0
        return {simdutf::TOO_LONG, pos};
1151
0
      }
1152
0
    }
1153
    // todo: we reset the output to utf32 instead of using std::get<2.(ret) as
1154
    // you'd expect. that is because
1155
    // validating_utf8_to_fixed_length_with_constant_checks may have processed
1156
    // data beyond the error.
1157
0
    result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(
1158
0
        pos, buf + pos, len - pos, utf32);
1159
0
    res.count += pos;
1160
0
    return res;
1161
0
  }
1162
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1163
0
  const char *end = buf + len;
1164
0
  if (std::get<0>(ret) == end) {
1165
0
    return {simdutf::SUCCESS, saved_bytes};
1166
0
  }
1167
1168
  // Note: the AVX512 procedure looks up 4 bytes forward, and
1169
  //       correctly converts multi-byte chars even if their
1170
  //       continuation bytes lie outside 16-byte window.
1171
  //       It means, we have to skip continuation bytes from
1172
  //       the beginning ret.first, as they were already consumed.
1173
0
  while (std::get<0>(ret) != end and
1174
0
         ((uint8_t(*std::get<0>(ret)) & 0xc0) == 0x80)) {
1175
0
    std::get<0>(ret) += 1;
1176
0
  }
1177
1178
0
  if (std::get<0>(ret) != end) {
1179
0
    auto scalar_result = scalar::utf8_to_utf32::convert_with_errors(
1180
0
        std::get<0>(ret), len - (std::get<0>(ret) - buf),
1181
0
        reinterpret_cast<char32_t *>(utf32_output) + saved_bytes);
1182
0
    if (scalar_result.error != simdutf::SUCCESS) {
1183
0
      scalar_result.count += (std::get<0>(ret) - buf);
1184
0
    } else {
1185
0
      scalar_result.count += saved_bytes;
1186
0
    }
1187
0
    return scalar_result;
1188
0
  }
1189
1190
0
  return {simdutf::SUCCESS, size_t(std::get<1>(ret) - utf32_output)};
1191
0
}
1192
1193
simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(
1194
0
    const char *buf, size_t len, char32_t *utf32_out) const noexcept {
1195
0
  uint32_t *utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
1196
0
  utf8_to_utf32_result ret =
1197
0
      icelake::valid_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(
1198
0
          buf, len, utf32_output);
1199
0
  size_t saved_bytes = ret.second - utf32_output;
1200
0
  const char *end = buf + len;
1201
0
  if (ret.first == end) {
1202
0
    return saved_bytes;
1203
0
  }
1204
1205
  // Note: AVX512 procedure looks up 4 bytes forward, and
1206
  //       correctly converts multi-byte chars even if their
1207
  //       continuation bytes lie outsiede 16-byte window.
1208
  //       It meas, we have to skip continuation bytes from
1209
  //       the beginning ret.first, as they were already consumed.
1210
0
  while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
1211
0
    ret.first += 1;
1212
0
  }
1213
1214
0
  if (ret.first != end) {
1215
0
    const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert_valid(
1216
0
        ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
1217
0
    if (scalar_saved_bytes == 0) {
1218
0
      return 0;
1219
0
    }
1220
0
    saved_bytes += scalar_saved_bytes;
1221
0
  }
1222
1223
0
  return saved_bytes;
1224
0
}
1225
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1226
1227
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
1228
simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(
1229
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1230
0
  return icelake_convert_utf16_to_latin1<endianness::LITTLE>(buf, len,
1231
0
                                                             latin1_output);
1232
0
}
1233
1234
simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(
1235
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1236
0
  return icelake_convert_utf16_to_latin1<endianness::BIG>(buf, len,
1237
0
                                                          latin1_output);
1238
0
}
1239
1240
simdutf_warn_unused result
1241
implementation::convert_utf16le_to_latin1_with_errors(
1242
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1243
0
  return icelake_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(
1244
0
             buf, len, latin1_output)
1245
0
      .first;
1246
0
}
1247
1248
simdutf_warn_unused result
1249
implementation::convert_utf16be_to_latin1_with_errors(
1250
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1251
0
  return icelake_convert_utf16_to_latin1_with_errors<endianness::BIG>(
1252
0
             buf, len, latin1_output)
1253
0
      .first;
1254
0
}
1255
1256
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(
1257
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1258
  // optimization opportunity: implement custom function
1259
0
  return convert_utf16be_to_latin1(buf, len, latin1_output);
1260
0
}
1261
1262
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(
1263
0
    const char16_t *buf, size_t len, char *latin1_output) const noexcept {
1264
  // optimization opportunity: implement custom function
1265
0
  return convert_utf16le_to_latin1(buf, len, latin1_output);
1266
0
}
1267
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
1268
1269
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1270
simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(
1271
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1272
0
  size_t outlen;
1273
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::LITTLE>(
1274
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1275
0
  if (inlen != len) {
1276
0
    return 0;
1277
0
  }
1278
0
  return outlen;
1279
0
}
1280
1281
simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(
1282
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1283
0
  size_t outlen;
1284
0
  size_t inlen = utf16_to_utf8_avx512i<endianness::BIG>(
1285
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1286
0
  if (inlen != len) {
1287
0
    return 0;
1288
0
  }
1289
0
  return outlen;
1290
0
}
1291
1292
template <endianness big_endian>
1293
simdutf_really_inline full_result convert_utf16_to_utf8_with_details(
1294
0
    const char16_t *buf, size_t len, char *utf8_output) {
1295
0
  size_t outlen;
1296
0
  size_t inlen = utf16_to_utf8_avx512i<big_endian>(
1297
0
      buf, len, (unsigned char *)utf8_output, &outlen);
1298
0
  if (inlen != len) {
1299
0
    full_result res =
1300
0
        scalar::utf16_to_utf8::convert_with_errors<big_endian, false>(
1301
0
            buf + inlen, len - inlen, utf8_output + outlen, 0);
1302
0
    return full_result(res.error, inlen + res.input_count,
1303
0
                       outlen + res.output_count);
1304
0
  }
1305
0
  return full_result(error_code::SUCCESS, len, outlen);
1306
0
}
Unexecuted instantiation: simdutf::full_result simdutf::icelake::convert_utf16_to_utf8_with_details<(simdutf::endianness)0>(char16_t const*, unsigned long, char*)
Unexecuted instantiation: simdutf::full_result simdutf::icelake::convert_utf16_to_utf8_with_details<(simdutf::endianness)1>(char16_t const*, unsigned long, char*)
1307
1308
simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(
1309
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1310
0
  return convert_utf16_to_utf8_with_details<endianness::LITTLE>(buf, len,
1311
0
                                                                utf8_output);
1312
0
}
1313
1314
simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(
1315
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1316
0
  return convert_utf16_to_utf8_with_details<endianness::BIG>(buf, len,
1317
0
                                                             utf8_output);
1318
0
}
1319
1320
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(
1321
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1322
0
  return convert_utf16le_to_utf8(buf, len, utf8_output);
1323
0
}
1324
1325
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(
1326
0
    const char16_t *buf, size_t len, char *utf8_output) const noexcept {
1327
0
  return convert_utf16be_to_utf8(buf, len, utf8_output);
1328
0
}
1329
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1330
1331
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
1332
simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(
1333
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1334
0
  return icelake_convert_utf32_to_latin1(buf, len, latin1_output);
1335
0
}
1336
1337
simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(
1338
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1339
0
  return icelake_convert_utf32_to_latin1_with_errors(buf, len, latin1_output)
1340
0
      .first;
1341
0
}
1342
1343
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(
1344
0
    const char32_t *buf, size_t len, char *latin1_output) const noexcept {
1345
0
  return icelake_convert_utf32_to_latin1(buf, len, latin1_output);
1346
0
}
1347
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
1348
1349
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1350
simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(
1351
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1352
0
  std::pair<const char32_t *, char *> ret =
1353
0
      avx512_convert_utf32_to_utf8(buf, len, utf8_output);
1354
0
  if (ret.first == nullptr) {
1355
0
    return 0;
1356
0
  }
1357
0
  size_t saved_bytes = ret.second - utf8_output;
1358
0
  if (ret.first != buf + len) {
1359
0
    const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
1360
0
        ret.first, len - (ret.first - buf), ret.second);
1361
0
    if (scalar_saved_bytes == 0) {
1362
0
      return 0;
1363
0
    }
1364
0
    saved_bytes += scalar_saved_bytes;
1365
0
  }
1366
0
  return saved_bytes;
1367
0
}
1368
1369
simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(
1370
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1371
  // ret.first.count is always the position in the buffer, not the number of
1372
  // code units written even if finished
1373
0
  std::pair<result, char *> ret =
1374
0
      icelake::avx512_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
1375
0
  if (ret.first.count != len) {
1376
0
    result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
1377
0
        buf + ret.first.count, len - ret.first.count, ret.second);
1378
0
    if (scalar_res.error) {
1379
0
      scalar_res.count += ret.first.count;
1380
0
      return scalar_res;
1381
0
    } else {
1382
0
      ret.second += scalar_res.count;
1383
0
    }
1384
0
  }
1385
0
  ret.first.count =
1386
0
      ret.second -
1387
0
      utf8_output; // Set count to the number of 8-bit code units written
1388
0
  return ret.first;
1389
0
}
1390
1391
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(
1392
0
    const char32_t *buf, size_t len, char *utf8_output) const noexcept {
1393
0
  return convert_utf32_to_utf8(buf, len, utf8_output);
1394
0
}
1395
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1396
1397
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1398
simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(
1399
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1400
0
  std::pair<const char32_t *, char16_t *> ret =
1401
0
      avx512_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
1402
0
  if (ret.first == nullptr) {
1403
0
    return 0;
1404
0
  }
1405
0
  size_t saved_bytes = ret.second - utf16_output;
1406
0
  return saved_bytes;
1407
0
}
1408
1409
simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(
1410
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1411
0
  std::pair<const char32_t *, char16_t *> ret =
1412
0
      avx512_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
1413
0
  if (ret.first == nullptr) {
1414
0
    return 0;
1415
0
  }
1416
0
  size_t saved_bytes = ret.second - utf16_output;
1417
0
  return saved_bytes;
1418
0
}
1419
1420
simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(
1421
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1422
  // ret.first.count is always the position in the buffer, not the number of
1423
  // code units written even if finished
1424
0
  std::pair<result, char16_t *> ret =
1425
0
      avx512_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(
1426
0
          buf, len, utf16_output);
1427
0
  if (ret.first.error) {
1428
0
    return ret.first;
1429
0
  }
1430
0
  ret.first.count =
1431
0
      ret.second -
1432
0
      utf16_output; // Set count to the number of 8-bit code units written
1433
0
  return ret.first;
1434
0
}
1435
1436
simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(
1437
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1438
  // ret.first.count is always the position in the buffer, not the number of
1439
  // code units written even if finished
1440
0
  std::pair<result, char16_t *> ret =
1441
0
      avx512_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len,
1442
0
                                                                 utf16_output);
1443
0
  if (ret.first.error) {
1444
0
    return ret.first;
1445
0
  }
1446
0
  ret.first.count =
1447
0
      ret.second -
1448
0
      utf16_output; // Set count to the number of 8-bit code units written
1449
0
  return ret.first;
1450
0
}
1451
1452
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(
1453
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1454
0
  return convert_utf32_to_utf16le(buf, len, utf16_output);
1455
0
}
1456
1457
simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(
1458
0
    const char32_t *buf, size_t len, char16_t *utf16_output) const noexcept {
1459
0
  return convert_utf32_to_utf16be(buf, len, utf16_output);
1460
0
}
1461
1462
simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(
1463
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1464
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1465
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1466
0
                                                          utf32_output);
1467
0
  if (!std::get<2>(ret)) {
1468
0
    return 0;
1469
0
  }
1470
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1471
0
  if (std::get<0>(ret) != buf + len) {
1472
0
    const size_t scalar_saved_bytes =
1473
0
        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
1474
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1475
0
    if (scalar_saved_bytes == 0) {
1476
0
      return 0;
1477
0
    }
1478
0
    saved_bytes += scalar_saved_bytes;
1479
0
  }
1480
0
  return saved_bytes;
1481
0
}
1482
1483
simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(
1484
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1485
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1486
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1487
0
  if (!std::get<2>(ret)) {
1488
0
    return 0;
1489
0
  }
1490
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1491
0
  if (std::get<0>(ret) != buf + len) {
1492
0
    const size_t scalar_saved_bytes =
1493
0
        scalar::utf16_to_utf32::convert<endianness::BIG>(
1494
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1495
0
    if (scalar_saved_bytes == 0) {
1496
0
      return 0;
1497
0
    }
1498
0
    saved_bytes += scalar_saved_bytes;
1499
0
  }
1500
0
  return saved_bytes;
1501
0
}
1502
1503
simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(
1504
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1505
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1506
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1507
0
                                                          utf32_output);
1508
0
  if (!std::get<2>(ret)) {
1509
0
    result scalar_res =
1510
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
1511
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1512
0
    scalar_res.count += (std::get<0>(ret) - buf);
1513
0
    return scalar_res;
1514
0
  }
1515
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1516
0
  if (std::get<0>(ret) != buf + len) {
1517
0
    result scalar_res =
1518
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
1519
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1520
0
    if (scalar_res.error) {
1521
0
      scalar_res.count += (std::get<0>(ret) - buf);
1522
0
      return scalar_res;
1523
0
    } else {
1524
0
      scalar_res.count += saved_bytes;
1525
0
      return scalar_res;
1526
0
    }
1527
0
  }
1528
0
  return simdutf::result(simdutf::SUCCESS, saved_bytes);
1529
0
}
1530
1531
simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(
1532
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1533
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1534
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1535
0
  if (!std::get<2>(ret)) {
1536
0
    result scalar_res =
1537
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
1538
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1539
0
    scalar_res.count += (std::get<0>(ret) - buf);
1540
0
    return scalar_res;
1541
0
  }
1542
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1543
0
  if (std::get<0>(ret) != buf + len) {
1544
0
    result scalar_res =
1545
0
        scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
1546
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1547
0
    if (scalar_res.error) {
1548
0
      scalar_res.count += (std::get<0>(ret) - buf);
1549
0
      return scalar_res;
1550
0
    } else {
1551
0
      scalar_res.count += saved_bytes;
1552
0
      return scalar_res;
1553
0
    }
1554
0
  }
1555
0
  return simdutf::result(simdutf::SUCCESS, saved_bytes);
1556
0
}
1557
1558
simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(
1559
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1560
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1561
0
      icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len,
1562
0
                                                          utf32_output);
1563
0
  if (!std::get<2>(ret)) {
1564
0
    return 0;
1565
0
  }
1566
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1567
0
  if (std::get<0>(ret) != buf + len) {
1568
0
    const size_t scalar_saved_bytes =
1569
0
        scalar::utf16_to_utf32::convert<endianness::LITTLE>(
1570
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1571
0
    if (scalar_saved_bytes == 0) {
1572
0
      return 0;
1573
0
    }
1574
0
    saved_bytes += scalar_saved_bytes;
1575
0
  }
1576
0
  return saved_bytes;
1577
0
}
1578
1579
simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(
1580
0
    const char16_t *buf, size_t len, char32_t *utf32_output) const noexcept {
1581
0
  std::tuple<const char16_t *, char32_t *, bool> ret =
1582
0
      icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
1583
0
  if (!std::get<2>(ret)) {
1584
0
    return 0;
1585
0
  }
1586
0
  size_t saved_bytes = std::get<1>(ret) - utf32_output;
1587
0
  if (std::get<0>(ret) != buf + len) {
1588
0
    const size_t scalar_saved_bytes =
1589
0
        scalar::utf16_to_utf32::convert<endianness::BIG>(
1590
0
            std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
1591
0
    if (scalar_saved_bytes == 0) {
1592
0
      return 0;
1593
0
    }
1594
0
    saved_bytes += scalar_saved_bytes;
1595
0
  }
1596
0
  return saved_bytes;
1597
0
}
1598
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1599
1600
#if SIMDUTF_FEATURE_UTF16
1601
void implementation::change_endianness_utf16(const char16_t *input,
1602
                                             size_t length,
1603
0
                                             char16_t *output) const noexcept {
1604
0
  size_t pos = 0;
1605
0
  const __m512i byteflip = _mm512_setr_epi64(
1606
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
1607
0
      0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
1608
0
      0x0607040502030001, 0x0e0f0c0d0a0b0809);
1609
0
  while (pos + 32 <= length) {
1610
0
    __m512i utf16 = _mm512_loadu_si512((const __m512i *)(input + pos));
1611
0
    utf16 = _mm512_shuffle_epi8(utf16, byteflip);
1612
0
    _mm512_storeu_si512(output + pos, utf16);
1613
0
    pos += 32;
1614
0
  }
1615
0
  if (pos < length) {
1616
0
    __mmask32 m((1U << (length - pos)) - 1);
1617
0
    __m512i utf16 = _mm512_maskz_loadu_epi16(m, (const __m512i *)(input + pos));
1618
0
    utf16 = _mm512_shuffle_epi8(utf16, byteflip);
1619
0
    _mm512_mask_storeu_epi16(output + pos, m, utf16);
1620
0
  }
1621
0
}
1622
1623
simdutf_warn_unused size_t implementation::count_utf16le(
1624
0
    const char16_t *input, size_t length) const noexcept {
1625
0
  const char16_t *ptr = input;
1626
0
  size_t count{0};
1627
1628
0
  if (length >= 32) {
1629
0
    const char16_t *end = input + length - 32;
1630
1631
0
    const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
1632
0
    const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
1633
1634
0
    while (ptr <= end) {
1635
0
      __m512i utf16 = _mm512_loadu_si512((const __m512i *)ptr);
1636
0
      ptr += 32;
1637
0
      uint64_t not_high_surrogate =
1638
0
          static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) |
1639
0
                                _mm512_cmplt_epu16_mask(utf16, low));
1640
0
      count += count_ones(not_high_surrogate);
1641
0
    }
1642
0
  }
1643
1644
0
  return count + scalar::utf16::count_code_points<endianness::LITTLE>(
1645
0
                     ptr, length - (ptr - input));
1646
0
}
1647
1648
simdutf_warn_unused size_t implementation::count_utf16be(
1649
0
    const char16_t *input, size_t length) const noexcept {
1650
0
  const char16_t *ptr = input;
1651
0
  size_t count{0};
1652
0
  if (length >= 32) {
1653
1654
0
    const char16_t *end = input + length - 32;
1655
1656
0
    const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
1657
0
    const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
1658
1659
0
    const __m512i byteflip = _mm512_setr_epi64(
1660
0
        0x0607040502030001, 0x0e0f0c0d0a0b0809, 0x0607040502030001,
1661
0
        0x0e0f0c0d0a0b0809, 0x0607040502030001, 0x0e0f0c0d0a0b0809,
1662
0
        0x0607040502030001, 0x0e0f0c0d0a0b0809);
1663
0
    while (ptr <= end) {
1664
0
      __m512i utf16 =
1665
0
          _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i *)ptr), byteflip);
1666
0
      ptr += 32;
1667
0
      uint64_t not_high_surrogate =
1668
0
          static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) |
1669
0
                                _mm512_cmplt_epu16_mask(utf16, low));
1670
0
      count += count_ones(not_high_surrogate);
1671
0
    }
1672
0
  }
1673
1674
0
  return count + scalar::utf16::count_code_points<endianness::BIG>(
1675
0
                     ptr, length - (ptr - input));
1676
0
}
1677
#endif // SIMDUTF_FEATURE_UTF16
1678
1679
#if SIMDUTF_FEATURE_UTF8
1680
simdutf_warn_unused size_t
1681
0
implementation::count_utf8(const char *input, size_t length) const noexcept {
1682
0
  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
1683
0
  size_t answer =
1684
0
      length / sizeof(__m512i) *
1685
0
      sizeof(__m512i); // Number of 512-bit chunks that fits into the length.
1686
0
  size_t i = 0;
1687
0
  __m512i unrolled_popcount{0};
1688
1689
0
  const __m512i continuation = _mm512_set1_epi8(char(0b10111111));
1690
1691
0
  while (i + sizeof(__m512i) <= length) {
1692
0
    size_t iterations = (length - i) / sizeof(__m512i);
1693
1694
0
    size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
1695
0
    for (; i + 8 * sizeof(__m512i) <= max_i; i += 8 * sizeof(__m512i)) {
1696
0
      __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
1697
0
      __m512i input2 =
1698
0
          _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
1699
0
      __m512i input3 =
1700
0
          _mm512_loadu_si512((const __m512i *)(str + i + 2 * sizeof(__m512i)));
1701
0
      __m512i input4 =
1702
0
          _mm512_loadu_si512((const __m512i *)(str + i + 3 * sizeof(__m512i)));
1703
0
      __m512i input5 =
1704
0
          _mm512_loadu_si512((const __m512i *)(str + i + 4 * sizeof(__m512i)));
1705
0
      __m512i input6 =
1706
0
          _mm512_loadu_si512((const __m512i *)(str + i + 5 * sizeof(__m512i)));
1707
0
      __m512i input7 =
1708
0
          _mm512_loadu_si512((const __m512i *)(str + i + 6 * sizeof(__m512i)));
1709
0
      __m512i input8 =
1710
0
          _mm512_loadu_si512((const __m512i *)(str + i + 7 * sizeof(__m512i)));
1711
1712
0
      __mmask64 mask1 = _mm512_cmple_epi8_mask(input1, continuation);
1713
0
      __mmask64 mask2 = _mm512_cmple_epi8_mask(input2, continuation);
1714
0
      __mmask64 mask3 = _mm512_cmple_epi8_mask(input3, continuation);
1715
0
      __mmask64 mask4 = _mm512_cmple_epi8_mask(input4, continuation);
1716
0
      __mmask64 mask5 = _mm512_cmple_epi8_mask(input5, continuation);
1717
0
      __mmask64 mask6 = _mm512_cmple_epi8_mask(input6, continuation);
1718
0
      __mmask64 mask7 = _mm512_cmple_epi8_mask(input7, continuation);
1719
0
      __mmask64 mask8 = _mm512_cmple_epi8_mask(input8, continuation);
1720
1721
0
      __m512i mask_register = _mm512_set_epi64(mask8, mask7, mask6, mask5,
1722
0
                                               mask4, mask3, mask2, mask1);
1723
1724
0
      unrolled_popcount = _mm512_add_epi64(unrolled_popcount,
1725
0
                                           _mm512_popcnt_epi64(mask_register));
1726
0
    }
1727
1728
0
    for (; i <= max_i; i += sizeof(__m512i)) {
1729
0
      __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
1730
0
      uint64_t continuation_bitmask = static_cast<uint64_t>(
1731
0
          _mm512_cmple_epi8_mask(more_input, continuation));
1732
0
      answer -= count_ones(continuation_bitmask);
1733
0
    }
1734
0
  }
1735
1736
0
  answer -= _mm512_reduce_add_epi64(unrolled_popcount);
1737
1738
0
  return answer + scalar::utf8::count_code_points(
1739
0
                      reinterpret_cast<const char *>(str + i), length - i);
1740
0
}
1741
#endif // SIMDUTF_FEATURE_UTF8
1742
1743
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1744
simdutf_warn_unused size_t implementation::latin1_length_from_utf8(
1745
0
    const char *buf, size_t len) const noexcept {
1746
0
  return count_utf8(buf, len);
1747
0
}
1748
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1749
1750
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1751
simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(
1752
0
    const char16_t *input, size_t length) const noexcept {
1753
0
  return icelake_utf8_length_from_utf16<endianness::LITTLE>(input, length);
1754
0
}
1755
1756
simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(
1757
0
    const char16_t *input, size_t length) const noexcept {
1758
0
  return icelake_utf8_length_from_utf16<endianness::BIG>(input, length);
1759
0
}
1760
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1761
1762
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1763
simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(
1764
0
    const char16_t *input, size_t length) const noexcept {
1765
0
  return implementation::count_utf16le(input, length);
1766
0
}
1767
1768
simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(
1769
0
    const char16_t *input, size_t length) const noexcept {
1770
0
  return implementation::count_utf16be(input, length);
1771
0
}
1772
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1773
1774
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1775
simdutf_warn_unused size_t implementation::utf8_length_from_latin1(
1776
0
    const char *input, size_t length) const noexcept {
1777
0
  const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
1778
0
  size_t answer = length / sizeof(__m512i) * sizeof(__m512i);
1779
0
  size_t i = 0;
1780
0
  if (answer >= 2048) // long strings optimization
1781
0
  {
1782
0
    unsigned char v_0xFF = 0xff;
1783
0
    __m512i eight_64bits = _mm512_setzero_si512();
1784
0
    while (i + sizeof(__m512i) <= length) {
1785
0
      __m512i runner = _mm512_setzero_si512();
1786
0
      size_t iterations = (length - i) / sizeof(__m512i);
1787
0
      if (iterations > 255) {
1788
0
        iterations = 255;
1789
0
      }
1790
0
      size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
1791
0
      for (; i + 4 * sizeof(__m512i) <= max_i; i += 4 * sizeof(__m512i)) {
1792
        // Load four __m512i vectors
1793
0
        __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
1794
0
        __m512i input2 =
1795
0
            _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
1796
0
        __m512i input3 = _mm512_loadu_si512(
1797
0
            (const __m512i *)(str + i + 2 * sizeof(__m512i)));
1798
0
        __m512i input4 = _mm512_loadu_si512(
1799
0
            (const __m512i *)(str + i + 3 * sizeof(__m512i)));
1800
1801
        // Generate four masks
1802
0
        __mmask64 mask1 =
1803
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input1);
1804
0
        __mmask64 mask2 =
1805
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input2);
1806
0
        __mmask64 mask3 =
1807
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input3);
1808
0
        __mmask64 mask4 =
1809
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input4);
1810
        // Apply the masks and subtract from the runner
1811
0
        __m512i not_ascii1 =
1812
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask1, v_0xFF);
1813
0
        __m512i not_ascii2 =
1814
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask2, v_0xFF);
1815
0
        __m512i not_ascii3 =
1816
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask3, v_0xFF);
1817
0
        __m512i not_ascii4 =
1818
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask4, v_0xFF);
1819
1820
0
        runner = _mm512_sub_epi8(runner, not_ascii1);
1821
0
        runner = _mm512_sub_epi8(runner, not_ascii2);
1822
0
        runner = _mm512_sub_epi8(runner, not_ascii3);
1823
0
        runner = _mm512_sub_epi8(runner, not_ascii4);
1824
0
      }
1825
1826
0
      for (; i <= max_i; i += sizeof(__m512i)) {
1827
0
        __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
1828
1829
0
        __mmask64 mask =
1830
0
            _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), more_input);
1831
0
        __m512i not_ascii =
1832
0
            _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask, v_0xFF);
1833
0
        runner = _mm512_sub_epi8(runner, not_ascii);
1834
0
      }
1835
1836
0
      eight_64bits = _mm512_add_epi64(
1837
0
          eight_64bits, _mm512_sad_epu8(runner, _mm512_setzero_si512()));
1838
0
    }
1839
1840
0
    answer += _mm512_reduce_add_epi64(eight_64bits);
1841
0
  } else if (answer > 0) {
1842
0
    for (; i + sizeof(__m512i) <= length; i += sizeof(__m512i)) {
1843
0
      __m512i latin = _mm512_loadu_si512((const __m512i *)(str + i));
1844
0
      uint64_t non_ascii = _mm512_movepi8_mask(latin);
1845
0
      answer += count_ones(non_ascii);
1846
0
    }
1847
0
  }
1848
0
  return answer + scalar::latin1::utf8_length_from_latin1(
1849
0
                      reinterpret_cast<const char *>(str + i), length - i);
1850
0
}
1851
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
1852
1853
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1854
simdutf_warn_unused size_t implementation::utf16_length_from_utf8(
1855
0
    const char *input, size_t length) const noexcept {
1856
0
  size_t pos = 0;
1857
1858
  // UTF-16 char length based on the four most significant bits of UTF-8 bytes
1859
0
  const __m128i utf8_length_128 = _mm_setr_epi8(
1860
      // ASCII chars
1861
0
      /* 0000 */ 1,
1862
0
      /* 0001 */ 1,
1863
0
      /* 0010 */ 1,
1864
0
      /* 0011 */ 1,
1865
0
      /* 0100 */ 1,
1866
0
      /* 0101 */ 1,
1867
0
      /* 0110 */ 1,
1868
0
      /* 0111 */ 1,
1869
1870
      // continuation bytes
1871
      /* 1000 */ 0,
1872
0
      /* 1001 */ 0,
1873
0
      /* 1010 */ 0,
1874
0
      /* 1011 */ 0,
1875
1876
      // leading bytes
1877
      /* 1100 */ 1, // 2-byte UTF-8 char => 1 UTF-16 word
1878
0
      /* 1101 */ 1, // 2-byte UTF-8 char => 1 UTF-16 word
1879
0
      /* 1110 */ 1, // 3-byte UTF-8 char => 1 UTF-16 word
1880
0
      /* 1111 */ 2  // 4-byte UTF-8 char => 2 UTF-16 words (surrogate pair)
1881
0
  );
1882
1883
0
  const __m512i char_length = broadcast_128bit_lane(utf8_length_128);
1884
1885
0
  constexpr size_t max_iterations = 255 / 2;
1886
1887
0
  size_t iterations = 0;
1888
0
  const auto zero = _mm512_setzero_si512();
1889
0
  __m512i local = _mm512_setzero_si512();    // byte-wise counters
1890
0
  __m512i counters = _mm512_setzero_si512(); // 64-bit counters
1891
0
  for (; pos + 64 <= length; pos += 64) {
1892
0
    __m512i utf8 = _mm512_loadu_si512((const __m512i *)(input + pos));
1893
0
    const auto t0 = _mm512_srli_epi32(utf8, 4);
1894
0
    const auto t1 = _mm512_and_si512(t0, _mm512_set1_epi8(0xf));
1895
0
    const auto t2 = _mm512_shuffle_epi8(char_length, t1);
1896
0
    local = _mm512_add_epi8(local, t2);
1897
1898
0
    iterations += 1;
1899
0
    if (iterations == max_iterations) {
1900
0
      counters = _mm512_add_epi64(counters, _mm512_sad_epu8(local, zero));
1901
0
      local = zero;
1902
0
      iterations = 0;
1903
0
    }
1904
0
  }
1905
1906
0
  size_t count = 0;
1907
1908
0
  if (pos > 0) {
1909
    // don't waste time for short strings
1910
0
    if (iterations > 0) {
1911
0
      counters = _mm512_add_epi64(counters, _mm512_sad_epu8(local, zero));
1912
0
    }
1913
1914
0
    const auto l0 = _mm512_extracti32x4_epi32(counters, 0);
1915
0
    const auto l1 = _mm512_extracti32x4_epi32(counters, 1);
1916
0
    const auto l2 = _mm512_extracti32x4_epi32(counters, 2);
1917
0
    const auto l3 = _mm512_extracti32x4_epi32(counters, 3);
1918
1919
0
    const auto sum =
1920
0
        _mm_add_epi64(_mm_add_epi64(l0, l1), _mm_add_epi64(l2, l3));
1921
1922
0
    count = uint64_t(_mm_extract_epi64(sum, 0)) +
1923
0
            uint64_t(_mm_extract_epi64(sum, 1));
1924
0
  }
1925
1926
0
  return count +
1927
0
         scalar::utf8::utf16_length_from_utf8(input + pos, length - pos);
1928
0
}
1929
simdutf_warn_unused result
1930
implementation::utf8_length_from_utf16le_with_replacement(
1931
0
    const char16_t *input, size_t length) const noexcept {
1932
0
  return icelake_utf8_length_from_utf16_with_replacement<endianness::LITTLE>(
1933
0
      input, length);
1934
0
}
1935
1936
simdutf_warn_unused result
1937
implementation::utf8_length_from_utf16be_with_replacement(
1938
0
    const char16_t *input, size_t length) const noexcept {
1939
0
  return icelake_utf8_length_from_utf16_with_replacement<endianness::BIG>(
1940
0
      input, length);
1941
0
}
1942
1943
simdutf_warn_unused size_t
1944
implementation::convert_utf16le_to_utf8_with_replacement(
1945
0
    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
1946
0
  return utf16_to_utf8::convert_with_replacement_via(
1947
0
      [](const char16_t *b, size_t l, char *o) {
1948
0
        return convert_utf16_to_utf8_with_details<endianness::LITTLE>(b, l, o);
1949
0
      },
1950
0
      input, length, utf8_buffer);
1951
0
}
1952
1953
simdutf_warn_unused size_t
1954
implementation::convert_utf16be_to_utf8_with_replacement(
1955
0
    const char16_t *input, size_t length, char *utf8_buffer) const noexcept {
1956
0
  return utf16_to_utf8::convert_with_replacement_via(
1957
0
      [](const char16_t *b, size_t l, char *o) {
1958
0
        return convert_utf16_to_utf8_with_details<endianness::BIG>(b, l, o);
1959
0
      },
1960
0
      input, length, utf8_buffer);
1961
0
}
1962
1963
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
1964
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1965
simdutf_warn_unused size_t implementation::utf8_length_from_utf32(
1966
0
    const char32_t *input, size_t length) const noexcept {
1967
0
  return utf32::utf8_length_from_utf32(input, length);
1968
0
}
1969
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1970
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1971
simdutf_warn_unused size_t implementation::utf16_length_from_utf32(
1972
0
    const char32_t *input, size_t length) const noexcept {
1973
0
  const char32_t *ptr = input;
1974
0
  size_t count{0};
1975
1976
0
  if (length >= 16) {
1977
0
    const char32_t *end = input + length - 16;
1978
1979
0
    const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff);
1980
1981
0
    while (ptr <= end) {
1982
0
      __m512i utf32 = _mm512_loadu_si512((const __m512i *)ptr);
1983
0
      ptr += 16;
1984
0
      __mmask16 surrogates_bitmask =
1985
0
          _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff);
1986
1987
0
      count += 16 + count_ones(surrogates_bitmask);
1988
0
    }
1989
0
  }
1990
1991
0
  return count +
1992
0
         scalar::utf32::utf16_length_from_utf32(ptr, length - (ptr - input));
1993
0
}
1994
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
1995
1996
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
1997
simdutf_warn_unused size_t implementation::utf32_length_from_utf8(
1998
0
    const char *input, size_t length) const noexcept {
1999
0
  return implementation::count_utf8(input, length);
2000
0
}
2001
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
2002
2003
#if SIMDUTF_FEATURE_BASE64
2004
simdutf_warn_unused result implementation::base64_to_binary(
2005
    const char *input, size_t length, char *output, base64_options options,
2006
0
    last_chunk_handling_options last_chunk_options) const noexcept {
2007
0
  if (options & base64_default_or_url) {
2008
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
2009
0
      return compress_decode_base64<false, true, true>(
2010
0
          output, input, length, options, last_chunk_options);
2011
0
    } else {
2012
0
      return compress_decode_base64<false, false, true>(
2013
0
          output, input, length, options, last_chunk_options);
2014
0
    }
2015
0
  } else if (options & base64_url) {
2016
0
    if (options == base64_options::base64_url_accept_garbage) {
2017
0
      return compress_decode_base64<true, true, false>(
2018
0
          output, input, length, options, last_chunk_options);
2019
0
    } else {
2020
0
      return compress_decode_base64<true, false, false>(
2021
0
          output, input, length, options, last_chunk_options);
2022
0
    }
2023
0
  } else {
2024
0
    if (options == base64_options::base64_default_accept_garbage) {
2025
0
      return compress_decode_base64<false, true, false>(
2026
0
          output, input, length, options, last_chunk_options);
2027
0
    } else {
2028
0
      return compress_decode_base64<false, false, false>(
2029
0
          output, input, length, options, last_chunk_options);
2030
0
    }
2031
0
  }
2032
0
}
2033
2034
simdutf_warn_unused full_result implementation::base64_to_binary_details(
2035
    const char *input, size_t length, char *output, base64_options options,
2036
0
    last_chunk_handling_options last_chunk_options) const noexcept {
2037
0
  if (options & base64_default_or_url) {
2038
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
2039
0
      return compress_decode_base64<false, true, true>(
2040
0
          output, input, length, options, last_chunk_options);
2041
0
    } else {
2042
0
      return compress_decode_base64<false, false, true>(
2043
0
          output, input, length, options, last_chunk_options);
2044
0
    }
2045
0
  } else if (options & base64_url) {
2046
0
    if (options == base64_options::base64_url_accept_garbage) {
2047
0
      return compress_decode_base64<true, true, false>(
2048
0
          output, input, length, options, last_chunk_options);
2049
0
    } else {
2050
0
      return compress_decode_base64<true, false, false>(
2051
0
          output, input, length, options, last_chunk_options);
2052
0
    }
2053
0
  } else {
2054
0
    if (options == base64_options::base64_default_accept_garbage) {
2055
0
      return compress_decode_base64<false, true, false>(
2056
0
          output, input, length, options, last_chunk_options);
2057
0
    } else {
2058
0
      return compress_decode_base64<false, false, false>(
2059
0
          output, input, length, options, last_chunk_options);
2060
0
    }
2061
0
  }
2062
0
}
2063
2064
simdutf_warn_unused result implementation::base64_to_binary(
2065
    const char16_t *input, size_t length, char *output, base64_options options,
2066
0
    last_chunk_handling_options last_chunk_options) const noexcept {
2067
0
  if (options & base64_default_or_url) {
2068
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
2069
0
      return compress_decode_base64<false, true, true>(
2070
0
          output, input, length, options, last_chunk_options);
2071
0
    } else {
2072
0
      return compress_decode_base64<false, false, true>(
2073
0
          output, input, length, options, last_chunk_options);
2074
0
    }
2075
0
  } else if (options & base64_url) {
2076
0
    if (options == base64_options::base64_url_accept_garbage) {
2077
0
      return compress_decode_base64<true, true, false>(
2078
0
          output, input, length, options, last_chunk_options);
2079
0
    } else {
2080
0
      return compress_decode_base64<true, false, false>(
2081
0
          output, input, length, options, last_chunk_options);
2082
0
    }
2083
0
  } else {
2084
0
    if (options == base64_options::base64_default_accept_garbage) {
2085
0
      return compress_decode_base64<false, true, false>(
2086
0
          output, input, length, options, last_chunk_options);
2087
0
    } else {
2088
0
      return compress_decode_base64<false, false, false>(
2089
0
          output, input, length, options, last_chunk_options);
2090
0
    }
2091
0
  }
2092
0
}
2093
2094
simdutf_warn_unused full_result implementation::base64_to_binary_details(
2095
    const char16_t *input, size_t length, char *output, base64_options options,
2096
0
    last_chunk_handling_options last_chunk_options) const noexcept {
2097
0
  if (options & base64_default_or_url) {
2098
0
    if (options == base64_options::base64_default_or_url_accept_garbage) {
2099
0
      return compress_decode_base64<false, true, true>(
2100
0
          output, input, length, options, last_chunk_options);
2101
0
    } else {
2102
0
      return compress_decode_base64<false, false, true>(
2103
0
          output, input, length, options, last_chunk_options);
2104
0
    }
2105
0
  } else if (options & base64_url) {
2106
0
    if (options == base64_options::base64_url_accept_garbage) {
2107
0
      return compress_decode_base64<true, true, false>(
2108
0
          output, input, length, options, last_chunk_options);
2109
0
    } else {
2110
0
      return compress_decode_base64<true, false, false>(
2111
0
          output, input, length, options, last_chunk_options);
2112
0
    }
2113
0
  } else {
2114
0
    if (options == base64_options::base64_default_accept_garbage) {
2115
0
      return compress_decode_base64<false, true, false>(
2116
0
          output, input, length, options, last_chunk_options);
2117
0
    } else {
2118
0
      return compress_decode_base64<false, false, false>(
2119
0
          output, input, length, options, last_chunk_options);
2120
0
    }
2121
0
  }
2122
0
}
2123
2124
size_t implementation::binary_to_base64(const char *input, size_t length,
2125
                                        char *output,
2126
0
                                        base64_options options) const noexcept {
2127
0
  if (options & base64_url) {
2128
0
    return encode_base64<true>(output, input, length, options);
2129
0
  } else {
2130
0
    return encode_base64<false>(output, input, length, options);
2131
0
  }
2132
0
}
2133
2134
size_t implementation::binary_to_base64_with_lines(
2135
    const char *input, size_t length, char *output, size_t line_length,
2136
0
    base64_options options) const noexcept {
2137
0
  if (options & base64_url) {
2138
0
    return encode_base64_impl<true, true>(output, input, length, options,
2139
0
                                          line_length);
2140
0
  } else {
2141
0
    return encode_base64_impl<false, true>(output, input, length, options,
2142
0
                                           line_length);
2143
0
  }
2144
0
}
2145
2146
const char *implementation::find(const char *start, const char *end,
2147
0
                                 char character) const noexcept {
2148
0
  return util_find(start, end, character);
2149
0
}
2150
const char16_t *implementation::find(const char16_t *start, const char16_t *end,
2151
0
                                     char16_t character) const noexcept {
2152
0
  return util_find(start, end, character);
2153
0
}
2154
2155
simdutf_warn_unused size_t implementation::binary_length_from_base64(
2156
0
    const char *input, size_t length) const noexcept {
2157
0
  return icelake_binary_length_from_base64(input, length);
2158
0
}
2159
2160
simdutf_warn_unused size_t implementation::binary_length_from_base64(
2161
0
    const char16_t *input, size_t length) const noexcept {
2162
0
  return icelake_binary_length_from_base64(input, length);
2163
0
}
2164
#endif // SIMDUTF_FEATURE_BASE64
2165
2166
} // namespace SIMDUTF_IMPLEMENTATION
2167
} // namespace simdutf
2168
2169
#include "simdutf/icelake/end.h"