Coverage Report

Created: 2026-11-12 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/work/build/simdutf/simdutf.h
Line
Count
Source
1
/* auto-generated on 2026-10-27 19:06:49 -0400. Do not edit! */
2
/* begin file include/simdutf.h */
3
#ifndef SIMDUTF_H
4
#define SIMDUTF_H
5
#include <cstring>
6
7
/* begin file include/simdutf/compiler_check.h */
8
#ifndef SIMDUTF_COMPILER_CHECK_H
9
#define SIMDUTF_COMPILER_CHECK_H
10
11
#ifndef __cplusplus
12
  #error simdutf requires a C++ compiler
13
#endif
14
15
#ifndef SIMDUTF_CPLUSPLUS
16
  #if defined(_MSVC_LANG) && !defined(__clang__)
17
    #define SIMDUTF_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
18
  #else
19
    #define SIMDUTF_CPLUSPLUS __cplusplus
20
  #endif
21
#endif
22
23
// C++ 26
24
#if !defined(SIMDUTF_CPLUSPLUS26) && (SIMDUTF_CPLUSPLUS >= 202602L)
25
  #define SIMDUTF_CPLUSPLUS26 1
26
#endif
27
28
// C++ 23
29
#if !defined(SIMDUTF_CPLUSPLUS23) && (SIMDUTF_CPLUSPLUS >= 202302L)
30
  #define SIMDUTF_CPLUSPLUS23 1
31
#endif
32
33
// C++ 20
34
#if !defined(SIMDUTF_CPLUSPLUS20) && (SIMDUTF_CPLUSPLUS >= 202002L)
35
  #define SIMDUTF_CPLUSPLUS20 1
36
#endif
37
38
// C++ 17
39
#if !defined(SIMDUTF_CPLUSPLUS17) && (SIMDUTF_CPLUSPLUS >= 201703L)
40
  #define SIMDUTF_CPLUSPLUS17 1
41
#endif
42
43
// C++ 14
44
#if !defined(SIMDUTF_CPLUSPLUS14) && (SIMDUTF_CPLUSPLUS >= 201402L)
45
  #define SIMDUTF_CPLUSPLUS14 1
46
#endif
47
48
// C++ 11
49
#if !defined(SIMDUTF_CPLUSPLUS11) && (SIMDUTF_CPLUSPLUS >= 201103L)
50
  #define SIMDUTF_CPLUSPLUS11 1
51
#endif
52
53
#ifndef SIMDUTF_CPLUSPLUS17
54
  #error simdutf requires a compiler compliant with the C++17 standard
55
#endif
56
57
#endif // SIMDUTF_COMPILER_CHECK_H
58
/* end file include/simdutf/compiler_check.h */
59
/* begin file include/simdutf/common_defs.h */
60
#ifndef SIMDUTF_COMMON_DEFS_H
61
#define SIMDUTF_COMMON_DEFS_H
62
63
/* begin file include/simdutf/portability.h */
64
#ifndef SIMDUTF_PORTABILITY_H
65
#define SIMDUTF_PORTABILITY_H
66
67
68
#include <cfloat>
69
#include <cstddef>
70
#include <cstdint>
71
#include <cstdio>
72
#include <cstdlib>
73
#ifndef _WIN32
74
  // strcasecmp, strncasecmp
75
  #include <strings.h>
76
#endif
77
78
#if defined(__apple_build_version__)
79
  #if __apple_build_version__ < 14000000
80
    #define SIMDUTF_SPAN_DISABLED                                              \
81
      1 // apple-clang/13 doesn't support std::convertible_to
82
  #endif
83
#endif
84
85
#if SIMDUTF_CPLUSPLUS20
86
  #include <version>
87
  #if __cpp_concepts >= 201907L && __cpp_lib_span >= 202002L &&                \
88
      !defined(SIMDUTF_SPAN_DISABLED)
89
    #define SIMDUTF_SPAN 1
90
  #endif // __cpp_concepts >= 201907L && __cpp_lib_span >= 202002L
91
  #if __cpp_lib_atomic_ref >= 201806L
92
    #define SIMDUTF_ATOMIC_REF 1
93
  #endif // __cpp_lib_atomic_ref
94
  #if __has_cpp_attribute(maybe_unused) >= 201603L
95
    #define SIMDUTF_MAYBE_UNUSED_AVAILABLE 1
96
  #endif // __has_cpp_attribute(maybe_unused) >= 201603L
97
#endif
98
99
/**
100
 * We want to check that it is actually a little endian system at
101
 * compile-time.
102
 */
103
104
#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
105
  #define SIMDUTF_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
106
#elif defined(_WIN32)
107
  #define SIMDUTF_IS_BIG_ENDIAN 0
108
#else
109
  #if defined(__APPLE__) ||                                                    \
110
      defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined
111
                           // __ORDER_BIG_ENDIAN__
112
    #include <machine/endian.h>
113
  #elif defined(sun) ||                                                        \
114
      defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__)
115
    #include <sys/byteorder.h>
116
  #else // defined(__APPLE__) || defined(__FreeBSD__)
117
118
    #ifdef __has_include
119
      #if __has_include(<endian.h>)
120
        #include <endian.h>
121
      #endif //__has_include(<endian.h>)
122
    #endif   //__has_include
123
124
  #endif // defined(__APPLE__) || defined(__FreeBSD__)
125
126
  #ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__)
127
    #define SIMDUTF_IS_BIG_ENDIAN 0
128
  #endif
129
130
  #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
131
    #define SIMDUTF_IS_BIG_ENDIAN 0
132
  #else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
133
    #define SIMDUTF_IS_BIG_ENDIAN 1
134
  #endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
135
136
#endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
137
138
/**
139
 * At this point in time, SIMDUTF_IS_BIG_ENDIAN is defined.
140
 */
141
142
#ifdef _MSC_VER
143
  #define SIMDUTF_VISUAL_STUDIO 1
144
  /**
145
   * We want to differentiate carefully between
146
   * clang under visual studio and regular visual
147
   * studio.
148
   *
149
   * Under clang for Windows, we enable:
150
   *  * target pragmas so that part and only part of the
151
   *     code gets compiled for advanced instructions.
152
   *
153
   */
154
  #ifdef __clang__
155
    // clang under visual studio
156
    #define SIMDUTF_CLANG_VISUAL_STUDIO 1
157
  #else
158
    // just regular visual studio (best guess)
159
    #define SIMDUTF_REGULAR_VISUAL_STUDIO 1
160
  #endif // __clang__
161
#endif   // _MSC_VER
162
163
#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
164
  // https://en.wikipedia.org/wiki/C_alternative_tokens
165
  // This header should have no effect, except maybe
166
  // under Visual Studio.
167
  #include <iso646.h>
168
#endif
169
170
#if (defined(__x86_64__) || defined(_M_AMD64)) && !defined(_M_ARM64EC)
171
  #define SIMDUTF_IS_X86_64 1
172
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
173
  #define SIMDUTF_IS_ARM64 1
174
#elif defined(__PPC64__) || defined(_M_PPC64)
175
  #if defined(__VEC__) && defined(__ALTIVEC__) && defined(__POWER8_VECTOR__)
176
    #define SIMDUTF_IS_PPC64 1
177
  #endif
178
#elif defined(__s390__)
179
// s390 IBM system. Big endian.
180
#elif (defined(__riscv) || defined(__riscv__)) && __riscv_xlen == 64
181
  // RISC-V 64-bit
182
  #define SIMDUTF_IS_RISCV64 1
183
184
  // #if __riscv_v_intrinsic >= 1000000
185
  //   #define SIMDUTF_HAS_RVV_INTRINSICS 1
186
  //   #define SIMDUTF_HAS_RVV_TARGET_REGION 1
187
  // #elif ...
188
  //  Check for special compiler versions that implement pre v1.0 intrinsics
189
  #if __riscv_v_intrinsic >= 11000
190
    #define SIMDUTF_HAS_RVV_INTRINSICS 1
191
  #endif
192
193
  #define SIMDUTF_HAS_ZVBB_INTRINSICS                                          \
194
    0 // there is currently no way to detect this
195
196
  #if SIMDUTF_HAS_RVV_INTRINSICS && __riscv_vector &&                          \
197
      __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64
198
    // RISC-V V extension
199
    #define SIMDUTF_IS_RVV 1
200
    #if SIMDUTF_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000
201
      // RISC-V Vector Basic Bit-manipulation
202
      #define SIMDUTF_IS_ZVBB 1
203
    #endif
204
  #endif
205
206
#elif defined(__loongarch_lp64)
207
  #if defined(__loongarch_sx) && defined(__loongarch_asx)
208
    #define SIMDUTF_IS_LSX 1
209
    #define SIMDUTF_IS_LASX 1 // We can always run both
210
  #elif defined(__loongarch_sx)
211
    #define SIMDUTF_IS_LSX 1
212
    // Adjust for runtime dispatching support.
213
    #if defined(__GNUC__) && !defined(__clang__) &&                            \
214
        !defined(__INTEL_COMPILER) && !defined(__NVCOMPILER)
215
      #if __GNUC__ > 15 || (__GNUC__ == 15 && __GNUC_MINOR__ >= 0)
216
        // We are ok, we will support runtime dispatch for LASX.
217
      #else
218
        // We disable runtime dispatch for LASX, which means that we will not be
219
        // able to use LASX even if it is supported by the hardware. Loongson
220
        // users should update to GCC 15 or better.
221
        #define SIMDUTF_IMPLEMENTATION_LASX 0
222
      #endif
223
    #else
224
      // We are not using GCC, so we assume that we can support runtime dispatch
225
      // for LASX. https://godbolt.org/z/jcMnrjYhs
226
      #define SIMDUTF_IMPLEMENTATION_LASX 0
227
    #endif
228
  #endif
229
#else
230
  // The simdutf library is designed
231
  // for 64-bit processors and it seems that you are not
232
  // compiling for a known 64-bit platform. Please
233
  // use a 64-bit target such as x64 or 64-bit ARM for best performance.
234
  #define SIMDUTF_IS_32BITS 1
235
236
  // We do not support 32-bit platforms, but it can be
237
  // handy to identify them.
238
  #if defined(_M_IX86) || defined(__i386__)
239
    #define SIMDUTF_IS_X86_32BITS 1
240
  #elif defined(__arm__) || defined(_M_ARM)
241
    #define SIMDUTF_IS_ARM_32BITS 1
242
  #elif defined(__PPC__) || defined(_M_PPC)
243
    #define SIMDUTF_IS_PPC_32BITS 1
244
  #endif
245
246
#endif // defined(__x86_64__) || defined(_M_AMD64)
247
248
#ifdef SIMDUTF_IS_32BITS
249
  #ifndef SIMDUTF_NO_PORTABILITY_WARNING
250
  // In the future, we may want to warn users of 32-bit systems that
251
  // the simdutf does not support accelerated kernels for such systems.
252
  #endif // SIMDUTF_NO_PORTABILITY_WARNING
253
#endif   // SIMDUTF_IS_32BITS
254
255
// this is almost standard?
256
0
#define SIMDUTF_STRINGIFY_IMPLEMENTATION_(a) #a
257
0
#define SIMDUTF_STRINGIFY(a) SIMDUTF_STRINGIFY_IMPLEMENTATION_(a)
258
259
// Our fast kernels require 64-bit systems.
260
//
261
// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions.
262
// Furthermore, the number of SIMD registers is reduced.
263
//
264
// On 32-bit ARM, we would have smaller registers.
265
//
266
// The simdutf users should still have the fallback kernel. It is
267
// slower, but it should run everywhere.
268
269
//
270
// Enable valid runtime implementations, and select
271
// SIMDUTF_BUILTIN_IMPLEMENTATION
272
//
273
274
// We are going to use runtime dispatch.
275
#if defined(SIMDUTF_IS_X86_64) || defined(SIMDUTF_IS_LSX)
276
  #ifdef __clang__
277
    // clang does not have GCC push pop
278
    // warning: clang attribute push can't be used within a namespace in clang
279
    // up til 8.0 so SIMDUTF_TARGET_REGION and SIMDUTF_UNTARGET_REGION must be
280
    // *outside* of a namespace.
281
    #define SIMDUTF_TARGET_REGION(T)                                           \
282
      _Pragma(SIMDUTF_STRINGIFY(clang attribute push(                          \
283
          __attribute__((target(T))), apply_to = function)))
284
    #define SIMDUTF_UNTARGET_REGION _Pragma("clang attribute pop")
285
  #elif defined(__GNUC__)
286
    // GCC is easier
287
    #define SIMDUTF_TARGET_REGION(T)                                           \
288
      _Pragma("GCC push_options") _Pragma(SIMDUTF_STRINGIFY(GCC target(T)))
289
    #define SIMDUTF_UNTARGET_REGION _Pragma("GCC pop_options")
290
  #endif // clang then gcc
291
292
#endif // defined(SIMDUTF_IS_X86_64) || defined(SIMDUTF_IS_LSX)
293
294
// Default target region macros don't do anything.
295
#ifndef SIMDUTF_TARGET_REGION
296
  #define SIMDUTF_TARGET_REGION(T)
297
  #define SIMDUTF_UNTARGET_REGION
298
#endif
299
300
// Is threading enabled?
301
#if defined(_REENTRANT) || defined(_MT)
302
  #ifndef SIMDUTF_THREADS_ENABLED
303
    #define SIMDUTF_THREADS_ENABLED
304
  #endif
305
#endif
306
307
// workaround for large stack sizes under -O0.
308
// https://github.com/simdutf/simdutf/issues/691
309
#ifdef __APPLE__
310
  #ifndef __OPTIMIZE__
311
    // Apple systems have small stack sizes in secondary threads.
312
    // Lack of compiler optimization may generate high stack usage.
313
    // Users may want to disable threads for safety, but only when
314
    // in debug mode which we detect by the fact that the __OPTIMIZE__
315
    // macro is not defined.
316
    #undef SIMDUTF_THREADS_ENABLED
317
  #endif
318
#endif
319
320
#ifdef SIMDUTF_VISUAL_STUDIO
321
  // This is one case where we do not distinguish between
322
  // regular visual studio and clang under visual studio.
323
  // clang under Windows has _stricmp (like visual studio) but not strcasecmp
324
  // (as clang normally has)
325
  #define simdutf_strcasecmp _stricmp
326
  #define simdutf_strncasecmp _strnicmp
327
#else
328
  // The strcasecmp, strncasecmp, and strcasestr functions do not work with
329
  // multibyte strings (e.g. UTF-8). So they are only useful for ASCII in our
330
  // context.
331
  // https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings
332
  #define simdutf_strcasecmp strcasecmp
333
  #define simdutf_strncasecmp strncasecmp
334
#endif
335
336
#if defined(__GNUC__) && !defined(__clang__)
337
  #if __GNUC__ >= 11
338
    #define SIMDUTF_GCC11ORMORE 1
339
  #endif //  __GNUC__ >= 11
340
  #if __GNUC__ == 10
341
    #define SIMDUTF_GCC10 1
342
  #endif //  __GNUC__ == 10
343
  #if __GNUC__ < 10
344
    #define SIMDUTF_GCC9OROLDER 1
345
  #endif //  __GNUC__ == 10
346
#endif   // defined(__GNUC__) && !defined(__clang__)
347
348
#endif // SIMDUTF_PORTABILITY_H
349
/* end file include/simdutf/portability.h */
350
/* begin file include/simdutf/avx512.h */
351
#ifndef SIMDUTF_AVX512_H_
352
#define SIMDUTF_AVX512_H_
353
354
/*
355
    It's possible to override AVX512 settings with cmake DCMAKE_CXX_FLAGS.
356
357
    All preprocessor directives has form `SIMDUTF_HAS_AVX512{feature}`,
358
    where a feature is a code name for extensions.
359
360
    Please see the listing below to find which are supported.
361
*/
362
363
#ifndef SIMDUTF_HAS_AVX512F
364
  #if defined(__AVX512F__) && __AVX512F__ == 1
365
    #define SIMDUTF_HAS_AVX512F 1
366
  #endif
367
#endif
368
369
#ifndef SIMDUTF_HAS_AVX512DQ
370
  #if defined(__AVX512DQ__) && __AVX512DQ__ == 1
371
    #define SIMDUTF_HAS_AVX512DQ 1
372
  #endif
373
#endif
374
375
#ifndef SIMDUTF_HAS_AVX512IFMA
376
  #if defined(__AVX512IFMA__) && __AVX512IFMA__ == 1
377
    #define SIMDUTF_HAS_AVX512IFMA 1
378
  #endif
379
#endif
380
381
#ifndef SIMDUTF_HAS_AVX512CD
382
  #if defined(__AVX512CD__) && __AVX512CD__ == 1
383
    #define SIMDUTF_HAS_AVX512CD 1
384
  #endif
385
#endif
386
387
#ifndef SIMDUTF_HAS_AVX512BW
388
  #if defined(__AVX512BW__) && __AVX512BW__ == 1
389
    #define SIMDUTF_HAS_AVX512BW 1
390
  #endif
391
#endif
392
393
#ifndef SIMDUTF_HAS_AVX512VL
394
  #if defined(__AVX512VL__) && __AVX512VL__ == 1
395
    #define SIMDUTF_HAS_AVX512VL 1
396
  #endif
397
#endif
398
399
#ifndef SIMDUTF_HAS_AVX512VBMI
400
  #if defined(__AVX512VBMI__) && __AVX512VBMI__ == 1
401
    #define SIMDUTF_HAS_AVX512VBMI 1
402
  #endif
403
#endif
404
405
#ifndef SIMDUTF_HAS_AVX512VBMI2
406
  #if defined(__AVX512VBMI2__) && __AVX512VBMI2__ == 1
407
    #define SIMDUTF_HAS_AVX512VBMI2 1
408
  #endif
409
#endif
410
411
#ifndef SIMDUTF_HAS_AVX512VNNI
412
  #if defined(__AVX512VNNI__) && __AVX512VNNI__ == 1
413
    #define SIMDUTF_HAS_AVX512VNNI 1
414
  #endif
415
#endif
416
417
#ifndef SIMDUTF_HAS_AVX512BITALG
418
  #if defined(__AVX512BITALG__) && __AVX512BITALG__ == 1
419
    #define SIMDUTF_HAS_AVX512BITALG 1
420
  #endif
421
#endif
422
423
#ifndef SIMDUTF_HAS_AVX512VPOPCNTDQ
424
  #if defined(__AVX512VPOPCNTDQ__) && __AVX512VPOPCNTDQ__ == 1
425
    #define SIMDUTF_HAS_AVX512VPOPCNTDQ 1
426
  #endif
427
#endif
428
429
#endif // SIMDUTF_AVX512_H_
430
/* end file include/simdutf/avx512.h */
431
432
// Sometimes logging is useful, but we want it disabled by default
433
// and free of any logging code in release builds.
434
#ifdef SIMDUTF_LOGGING
435
  #include <cstdlib>
436
  #include <iostream>
437
  #define simdutf_log(msg)                                                     \
438
    std::cout << "[" << __FUNCTION__ << "]: " << msg << std::endl              \
439
              << "\t" << __FILE__ << ":" << __LINE__ << std::endl;
440
  #define simdutf_log_assert(cond, msg)                                        \
441
    do {                                                                       \
442
      if (!(cond)) {                                                           \
443
        std::cerr << "[" << __FUNCTION__ << "]: " << msg << std::endl          \
444
                  << "\t" << __FILE__ << ":" << __LINE__ << std::endl;         \
445
        std::abort();                                                          \
446
      }                                                                        \
447
    } while (0)
448
#else
449
  #define simdutf_log(msg)
450
  #define simdutf_log_assert(cond, msg)
451
#endif
452
453
#if SIMDUTF_CPLUSPLUS17
454
  #define simdutf_unused [[maybe_unused]]
455
#endif // SIMDUTF_CPLUSPLUS17
456
457
#if defined(SIMDUTF_REGULAR_VISUAL_STUDIO)
458
  #define SIMDUTF_DEPRECATED __declspec(deprecated)
459
460
  #define simdutf_really_inline __forceinline // really inline in release mode
461
  #define simdutf_always_inline __forceinline // always inline, no matter what
462
  #define simdutf_never_inline __declspec(noinline)
463
464
  #ifndef simdutf_unused
465
    #define simdutf_unused
466
  #endif // simdutf_unused
467
  #define simdutf_warn_unused
468
469
  #ifndef simdutf_likely
470
    #define simdutf_likely(x) x
471
  #endif
472
  #ifndef simdutf_unlikely
473
    #define simdutf_unlikely(x) x
474
  #endif
475
476
  #define SIMDUTF_PUSH_DISABLE_WARNINGS __pragma(warning(push))
477
  #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS __pragma(warning(push, 0))
478
  #define SIMDUTF_DISABLE_VS_WARNING(WARNING_NUMBER)                           \
479
    __pragma(warning(disable : WARNING_NUMBER))
480
  // Get rid of Intellisense-only warnings (Code Analysis)
481
  // Though __has_include is C++17, it is supported in Visual Studio 2017 or
482
  // better (_MSC_VER>=1910).
483
  #ifdef __has_include
484
    #if __has_include(<CppCoreCheck\Warnings.h>)
485
      #include <CppCoreCheck\Warnings.h>
486
      #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS                               \
487
        SIMDUTF_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
488
    #endif
489
  #endif
490
491
  #ifndef SIMDUTF_DISABLE_UNDESIRED_WARNINGS
492
    #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS
493
  #endif
494
495
  #define SIMDUTF_DISABLE_DEPRECATED_WARNING SIMDUTF_DISABLE_VS_WARNING(4996)
496
  #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING
497
  #define SIMDUTF_POP_DISABLE_WARNINGS __pragma(warning(pop))
498
  #define SIMDUTF_DISABLE_UNUSED_WARNING
499
#else // SIMDUTF_REGULAR_VISUAL_STUDIO
500
  #if defined(__OPTIMIZE__) || defined(NDEBUG)
501
    #define simdutf_really_inline inline __attribute__((always_inline))
502
  #else
503
    #define simdutf_really_inline inline
504
  #endif
505
  #define simdutf_always_inline                                                \
506
    inline __attribute__((always_inline)) // always inline, no matter what
507
  #define SIMDUTF_DEPRECATED __attribute__((deprecated))
508
  #define simdutf_never_inline inline __attribute__((noinline))
509
  #ifndef simdutf_unused
510
    #define simdutf_unused __attribute__((unused))
511
  #endif // simdutf_unused
512
  #define simdutf_warn_unused __attribute__((warn_unused_result))
513
514
  #ifndef simdutf_likely
515
0
    #define simdutf_likely(x) __builtin_expect(!!(x), 1)
516
  #endif
517
  #ifndef simdutf_unlikely
518
0
    #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
519
  #endif
520
  // clang-format off
521
  #define SIMDUTF_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
522
  // gcc doesn't seem to disable all warnings with all and extra, add warnings
523
  // here as necessary
524
  #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS                                    \
525
    SIMDUTF_PUSH_DISABLE_WARNINGS                                              \
526
    SIMDUTF_DISABLE_GCC_WARNING(-Weffc++)                                      \
527
    SIMDUTF_DISABLE_GCC_WARNING(-Wall)                                         \
528
    SIMDUTF_DISABLE_GCC_WARNING(-Wconversion)                                  \
529
    SIMDUTF_DISABLE_GCC_WARNING(-Wextra)                                       \
530
    SIMDUTF_DISABLE_GCC_WARNING(-Wattributes)                                  \
531
    SIMDUTF_DISABLE_GCC_WARNING(-Wimplicit-fallthrough)                        \
532
    SIMDUTF_DISABLE_GCC_WARNING(-Wnon-virtual-dtor)                            \
533
    SIMDUTF_DISABLE_GCC_WARNING(-Wreturn-type)                                 \
534
    SIMDUTF_DISABLE_GCC_WARNING(-Wshadow)                                      \
535
    SIMDUTF_DISABLE_GCC_WARNING(-Wunused-parameter)                            \
536
    SIMDUTF_DISABLE_GCC_WARNING(-Wunused-variable)
537
  #define SIMDUTF_PRAGMA(P) _Pragma(#P)
538
  #define SIMDUTF_DISABLE_GCC_WARNING(WARNING)                                 \
539
    SIMDUTF_PRAGMA(GCC diagnostic ignored #WARNING)
540
  #if defined(SIMDUTF_CLANG_VISUAL_STUDIO)
541
    #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS                                 \
542
      SIMDUTF_DISABLE_GCC_WARNING(-Wmicrosoft-include)
543
  #else
544
    #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS
545
  #endif
546
  #define SIMDUTF_DISABLE_DEPRECATED_WARNING                                   \
547
    SIMDUTF_DISABLE_GCC_WARNING(-Wdeprecated-declarations)
548
  #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING                              \
549
    SIMDUTF_DISABLE_GCC_WARNING(-Wstrict-overflow)
550
  #define SIMDUTF_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
551
  #define SIMDUTF_DISABLE_UNUSED_WARNING                                       \
552
    SIMDUTF_PUSH_DISABLE_WARNINGS                                              \
553
    SIMDUTF_DISABLE_GCC_WARNING(-Wunused-function)                             \
554
    SIMDUTF_DISABLE_GCC_WARNING(-Wunused-const-variable)
555
  // clang-format on
556
557
#endif // MSC_VER
558
559
// Will evaluate to constexpr in C++23 or later. This makes it possible to mark
560
// functions constexpr if the "if consteval" feature is available to use.
561
#if SIMDUTF_CPLUSPLUS23
562
  #define simdutf_constexpr23 constexpr
563
#else
564
  #define simdutf_constexpr23
565
#endif
566
567
#ifndef SIMDUTF_DLLIMPORTEXPORT
568
  #if defined(SIMDUTF_VISUAL_STUDIO) // Visual Studio
569
                                     /**
570
                                      * Windows users need to do some extra work when building
571
                                      * or using a dynamic library (DLL). When building, we need
572
                                      * to set SIMDUTF_DLLIMPORTEXPORT to __declspec(dllexport).
573
                                      * When *using* the DLL, the user needs to set
574
                                      * SIMDUTF_DLLIMPORTEXPORT __declspec(dllimport).
575
                                      *
576
                                      * Static libraries not need require such work.
577
                                      *
578
                                      * It does not matter here whether you are using
579
                                      * the regular visual studio or clang under visual
580
                                      * studio, you still need to handle these issues.
581
                                      *
582
                                      * Non-Windows systems do not have this complexity.
583
                                      */
584
    #if SIMDUTF_BUILDING_WINDOWS_DYNAMIC_LIBRARY
585
586
      // We set SIMDUTF_BUILDING_WINDOWS_DYNAMIC_LIBRARY when we build a DLL
587
      // under Windows. It should never happen that both
588
      // SIMDUTF_BUILDING_WINDOWS_DYNAMIC_LIBRARY and
589
      // SIMDUTF_USING_WINDOWS_DYNAMIC_LIBRARY are set.
590
      #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllexport)
591
    #elif SIMDUTF_USING_WINDOWS_DYNAMIC_LIBRARY
592
      // Windows user who call a dynamic library should set
593
      // SIMDUTF_USING_WINDOWS_DYNAMIC_LIBRARY to 1.
594
595
      #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllimport)
596
    #else
597
      // We assume by default static linkage
598
      #define SIMDUTF_DLLIMPORTEXPORT
599
    #endif
600
  #else // defined(SIMDUTF_VISUAL_STUDIO)
601
    // Non-Windows systems do not have this complexity.
602
    #define SIMDUTF_DLLIMPORTEXPORT
603
  #endif // defined(SIMDUTF_VISUAL_STUDIO)
604
#endif
605
606
#if SIMDUTF_MAYBE_UNUSED_AVAILABLE
607
  #define simdutf_maybe_unused [[maybe_unused]]
608
#else
609
  #define simdutf_maybe_unused
610
#endif
611
612
#endif // SIMDUTF_COMMON_DEFS_H
613
/* end file include/simdutf/common_defs.h */
614
/* begin file include/simdutf/encoding_types.h */
615
#ifndef SIMDUTF_ENCODING_TYPES_H
616
#define SIMDUTF_ENCODING_TYPES_H
617
#include <string_view>
618
619
#if !defined(SIMDUTF_NO_STD_TEXT_ENCODING) &&                                  \
620
    defined(__cpp_lib_text_encoding) && __cpp_lib_text_encoding >= 202306L
621
  #define SIMDUTF_HAS_STD_TEXT_ENCODING 1
622
  #include <text_encoding>
623
#endif
624
625
namespace simdutf {
626
627
enum encoding_type {
628
  UTF8 = 1,      // BOM 0xef 0xbb 0xbf
629
  UTF16_LE = 2,  // BOM 0xff 0xfe
630
  UTF16_BE = 4,  // BOM 0xfe 0xff
631
  UTF32_LE = 8,  // BOM 0xff 0xfe 0x00 0x00
632
  UTF32_BE = 16, // BOM 0x00 0x00 0xfe 0xff
633
  Latin1 = 32,
634
635
  unspecified = 0
636
};
637
638
#ifndef SIMDUTF_IS_BIG_ENDIAN
639
  #error "SIMDUTF_IS_BIG_ENDIAN needs to be defined."
640
#endif
641
642
enum endianness {
643
  LITTLE = 0,
644
  BIG = 1,
645
  NATIVE =
646
#if SIMDUTF_IS_BIG_ENDIAN
647
      BIG
648
#else
649
      LITTLE
650
#endif
651
};
652
653
simdutf_warn_unused simdutf_really_inline constexpr bool
654
0
match_system(endianness e) {
655
0
  return e == endianness::NATIVE;
656
0
}
657
658
simdutf_warn_unused std::string_view to_string(encoding_type bom);
659
660
// Note that BOM for UTF8 is discouraged.
661
namespace BOM {
662
663
/**
664
 * Checks for a BOM. If not, returns unspecified
665
 * @param byte          the string to process
666
 * @param length        the length of the string in code units
667
 * @return the corresponding encoding
668
 */
669
670
simdutf_warn_unused encoding_type check_bom(const uint8_t *byte, size_t length);
671
simdutf_warn_unused encoding_type check_bom(const char *byte, size_t length);
672
/**
673
 * Returns the size, in bytes, of the BOM for a given encoding type.
674
 * Note that UTF8 BOM are discouraged.
675
 * @param bom         the encoding type
676
 * @return the size in bytes of the corresponding BOM
677
 */
678
simdutf_warn_unused size_t bom_byte_size(encoding_type bom);
679
680
} // namespace BOM
681
682
#ifdef SIMDUTF_HAS_STD_TEXT_ENCODING
683
/**
684
 * Convert a simdutf encoding type to a std::text_encoding.
685
 *
686
 * @param enc  the simdutf encoding type
687
 * @return     the corresponding std::text_encoding, or
688
 *             std::text_encoding::id::unknown for unspecified/unsupported
689
 */
690
simdutf_warn_unused constexpr std::text_encoding
691
to_std_encoding(encoding_type enc) noexcept {
692
  switch (enc) {
693
  case UTF8:
694
    return std::text_encoding(std::text_encoding::id::UTF8);
695
  case UTF16_LE:
696
    return std::text_encoding(std::text_encoding::id::UTF16LE);
697
  case UTF16_BE:
698
    return std::text_encoding(std::text_encoding::id::UTF16BE);
699
  case UTF32_LE:
700
    return std::text_encoding(std::text_encoding::id::UTF32LE);
701
  case UTF32_BE:
702
    return std::text_encoding(std::text_encoding::id::UTF32BE);
703
  case Latin1:
704
    return std::text_encoding(std::text_encoding::id::ISOLatin1);
705
  case unspecified:
706
  default:
707
    return std::text_encoding(std::text_encoding::id::unknown);
708
  }
709
}
710
711
/**
712
 * Convert a std::text_encoding to a simdutf encoding type.
713
 *
714
 * @param enc  the std::text_encoding
715
 * @return     the corresponding simdutf encoding type, or
716
 *             encoding_type::unspecified if the encoding is not supported
717
 */
718
simdutf_warn_unused constexpr encoding_type
719
from_std_encoding(const std::text_encoding &enc) noexcept {
720
  switch (enc.mib()) {
721
  case std::text_encoding::id::UTF8:
722
    return UTF8;
723
  case std::text_encoding::id::UTF16LE:
724
    return UTF16_LE;
725
  case std::text_encoding::id::UTF16BE:
726
    return UTF16_BE;
727
  case std::text_encoding::id::UTF32LE:
728
    return UTF32_LE;
729
  case std::text_encoding::id::UTF32BE:
730
    return UTF32_BE;
731
  case std::text_encoding::id::ISOLatin1:
732
    return Latin1;
733
  default:
734
    return unspecified;
735
  }
736
}
737
738
/**
739
 * Get the native-endian UTF-16 encoding type for this system.
740
 *
741
 * @return UTF16_LE on little-endian systems, UTF16_BE on big-endian systems
742
 */
743
simdutf_warn_unused constexpr encoding_type native_utf16_encoding() noexcept {
744
  #if SIMDUTF_IS_BIG_ENDIAN
745
  return UTF16_BE;
746
  #else
747
  return UTF16_LE;
748
  #endif
749
}
750
751
/**
752
 * Get the native-endian UTF-32 encoding type for this system.
753
 *
754
 * @return UTF32_LE on little-endian systems, UTF32_BE on big-endian systems
755
 */
756
simdutf_warn_unused constexpr encoding_type native_utf32_encoding() noexcept {
757
  #if SIMDUTF_IS_BIG_ENDIAN
758
  return UTF32_BE;
759
  #else
760
  return UTF32_LE;
761
  #endif
762
}
763
764
/**
765
 * Convert a std::text_encoding to a simdutf encoding type,
766
 * using native endianness for UTF-16/UTF-32 without explicit endianness.
767
 *
768
 * When the input is std::text_encoding::id::UTF16 or UTF32 (without LE/BE
769
 * suffix), this returns the native-endian simdutf variant.
770
 *
771
 * @param enc  the std::text_encoding
772
 * @return     the corresponding simdutf encoding type, or
773
 *             encoding_type::unspecified if the encoding is not supported
774
 */
775
simdutf_warn_unused constexpr encoding_type
776
from_std_encoding_native(const std::text_encoding &enc) noexcept {
777
  switch (enc.mib()) {
778
  case std::text_encoding::id::UTF8:
779
    return UTF8;
780
  case std::text_encoding::id::UTF16:
781
    return native_utf16_encoding();
782
  case std::text_encoding::id::UTF16LE:
783
    return UTF16_LE;
784
  case std::text_encoding::id::UTF16BE:
785
    return UTF16_BE;
786
  case std::text_encoding::id::UTF32:
787
    return native_utf32_encoding();
788
  case std::text_encoding::id::UTF32LE:
789
    return UTF32_LE;
790
  case std::text_encoding::id::UTF32BE:
791
    return UTF32_BE;
792
  case std::text_encoding::id::ISOLatin1:
793
    return Latin1;
794
  default:
795
    return unspecified;
796
  }
797
}
798
#endif // SIMDUTF_HAS_STD_TEXT_ENCODING
799
800
} // namespace simdutf
801
#endif
802
/* end file include/simdutf/encoding_types.h */
803
/* begin file include/simdutf/error.h */
804
#ifndef SIMDUTF_ERROR_H
805
#define SIMDUTF_ERROR_H
806
#include <string_view>
807
808
namespace simdutf {
809
810
enum error_code {
811
  SUCCESS = 0,
812
  HEADER_BITS, // Any byte must have fewer than 5 header bits.
813
  TOO_SHORT,   // The leading byte must be followed by N-1 continuation bytes,
814
               // where N is the UTF-8 character length This is also the error
815
               // when the input is truncated.
816
  TOO_LONG,    // We either have too many consecutive continuation bytes or the
817
               // string starts with a continuation byte.
818
  OVERLONG, // The decoded character must be above U+7F for two-byte characters,
819
            // U+7FF for three-byte characters, and U+FFFF for four-byte
820
            // characters.
821
  TOO_LARGE, // The decoded character must be less than or equal to
822
             // U+10FFFF,less than or equal than U+7F for ASCII OR less than
823
             // equal than U+FF for Latin1
824
  SURROGATE, // The decoded character must be not be in U+D800...DFFF (UTF-8 or
825
             // UTF-32)
826
             // OR
827
             // a high surrogate must be followed by a low surrogate
828
             // and a low surrogate must be preceded by a high surrogate
829
             // (UTF-16)
830
             // OR
831
             // there must be no surrogate at all and one is
832
             // found (Latin1 functions)
833
             // OR
834
             // *specifically* for the function
835
             // utf8_length_from_utf16_with_replacement, a surrogate (whether
836
             // in error or not) has been found (I.e., whether we are in the
837
             // Basic Multilingual Plane or not).
838
  INVALID_BASE64_CHARACTER, // Found a character that cannot be part of a valid
839
                            // base64 string. This may include a misplaced
840
                            // padding character ('=').
841
  BASE64_INPUT_REMAINDER,   // The base64 input terminates with a single
842
                            // character, excluding padding (=). It is also used
843
                            // in strict mode when padding is not adequate.
844
  BASE64_EXTRA_BITS,        // The base64 input terminates with non-zero
845
                            // padding bits.
846
  OUTPUT_BUFFER_TOO_SMALL,  // The provided buffer is too small.
847
  OTHER                     // Not related to validation/transcoding.
848
};
849
850
0
inline std::string_view error_to_string(error_code code) noexcept {
851
0
  switch (code) {
852
0
  case SUCCESS:
853
0
    return "SUCCESS";
854
0
  case HEADER_BITS:
855
0
    return "HEADER_BITS";
856
0
  case TOO_SHORT:
857
0
    return "TOO_SHORT";
858
0
  case TOO_LONG:
859
0
    return "TOO_LONG";
860
0
  case OVERLONG:
861
0
    return "OVERLONG";
862
0
  case TOO_LARGE:
863
0
    return "TOO_LARGE";
864
0
  case SURROGATE:
865
0
    return "SURROGATE";
866
0
  case INVALID_BASE64_CHARACTER:
867
0
    return "INVALID_BASE64_CHARACTER";
868
0
  case BASE64_INPUT_REMAINDER:
869
0
    return "BASE64_INPUT_REMAINDER";
870
0
  case BASE64_EXTRA_BITS:
871
0
    return "BASE64_EXTRA_BITS";
872
0
  case OUTPUT_BUFFER_TOO_SMALL:
873
0
    return "OUTPUT_BUFFER_TOO_SMALL";
874
0
  default:
875
0
    return "OTHER";
876
0
  }
877
0
}
878
879
struct result {
880
  error_code error;
881
  size_t count; // In case of error, indicates the position of the error. In
882
                // case of success, indicates the number of code units
883
                // validated/written.
884
885
  simdutf_really_inline simdutf_constexpr23 result() noexcept
886
0
      : error{error_code::SUCCESS}, count{0} {}
887
888
  simdutf_really_inline simdutf_constexpr23 result(error_code err,
889
                                                   size_t pos) noexcept
890
22.7k
      : error{err}, count{pos} {}
891
892
22.7k
  simdutf_really_inline simdutf_constexpr23 bool is_ok() const noexcept {
893
22.7k
    return error == error_code::SUCCESS;
894
22.7k
  }
895
896
0
  simdutf_really_inline simdutf_constexpr23 bool is_err() const noexcept {
897
0
    return error != error_code::SUCCESS;
898
0
  }
899
};
900
901
struct full_result {
902
  error_code error;
903
  size_t input_count;
904
  size_t output_count;
905
  bool padding_error = false; // true if the error is due to padding, only
906
                              // meaningful when error is not SUCCESS
907
908
  simdutf_really_inline simdutf_constexpr23 full_result() noexcept
909
0
      : error{error_code::SUCCESS}, input_count{0}, output_count{0} {}
910
911
  simdutf_really_inline simdutf_constexpr23 full_result(error_code err,
912
                                                        size_t pos_in,
913
                                                        size_t pos_out) noexcept
914
22.6k
      : error{err}, input_count{pos_in}, output_count{pos_out} {}
915
  simdutf_really_inline simdutf_constexpr23 full_result(
916
      error_code err, size_t pos_in, size_t pos_out, bool padding_err) noexcept
917
36
      : error{err}, input_count{pos_in}, output_count{pos_out},
918
36
        padding_error{padding_err} {}
919
920
22.7k
  simdutf_really_inline simdutf_constexpr23 operator result() const noexcept {
921
22.7k
    if (error == error_code::SUCCESS) {
922
22.1k
      return result{error, output_count};
923
22.1k
    } else {
924
533
      return result{error, input_count};
925
533
    }
926
22.7k
  }
927
};
928
929
struct utf8_result {
930
  error_code error;
931
  size_t input_count;
932
  size_t continuation_count;
933
  size_t four_byte_count;
934
935
  simdutf_really_inline simdutf_constexpr23 utf8_result() noexcept
936
      : error{error_code::SUCCESS}, input_count{0}, continuation_count{0},
937
0
        four_byte_count{0} {}
938
939
  simdutf_really_inline simdutf_constexpr23
940
  utf8_result(error_code err, size_t pos_in, size_t continuation_count_,
941
              size_t four_byte_count_) noexcept
942
      : error{err}, input_count{pos_in},
943
        continuation_count{continuation_count_},
944
0
        four_byte_count{four_byte_count_} {}
945
946
  simdutf_really_inline simdutf_constexpr23 size_t
947
0
  utf16_length() const noexcept {
948
0
    return input_count - continuation_count + four_byte_count;
949
0
  }
950
951
  // The number of code points in the valid prefix, which is also the number of
952
  // UTF-32 code units.
953
  simdutf_really_inline simdutf_constexpr23 size_t
954
0
  utf32_length() const noexcept {
955
0
    return input_count - continuation_count;
956
0
  }
957
};
958
959
// UTF-16 size of a possibly ill-formed UTF-8 string, plus the first few
960
// ill-formed subsequences. Pass this to convert_utf8_to_utf16_with_replacement
961
// on the same bytes. error_offset entries are byte indexes from the start of
962
// that input. count is always the full number of char16_t. more_errors is
963
// true when the input has ill-formed subsequences past the stored ones.
964
struct utf8_to_utf16_result {
965
  static constexpr size_t max_errors = 16;
966
  error_code error;
967
  size_t count;
968
  size_t error_count;
969
  bool more_errors;
970
  size_t error_offset[max_errors];
971
972
  simdutf_really_inline simdutf_constexpr23 utf8_to_utf16_result() noexcept
973
      : error{error_code::SUCCESS}, count{0}, error_count{0},
974
0
        more_errors{false}, error_offset{} {}
975
};
976
977
} // namespace simdutf
978
#endif
979
/* end file include/simdutf/error.h */
980
981
SIMDUTF_PUSH_DISABLE_WARNINGS
982
SIMDUTF_DISABLE_UNDESIRED_WARNINGS
983
984
// Public API
985
/* begin file include/simdutf/simdutf_version.h */
986
// /include/simdutf/simdutf_version.h automatically generated by release.py,
987
// do not change by hand
988
#ifndef SIMDUTF_SIMDUTF_VERSION_H
989
#define SIMDUTF_SIMDUTF_VERSION_H
990
991
/** The version of simdutf being used (major.minor.revision) */
992
#define SIMDUTF_VERSION "9.3.3"
993
994
namespace simdutf {
995
enum {
996
  /**
997
   * The major version (MAJOR.minor.revision) of simdutf being used.
998
   */
999
  SIMDUTF_VERSION_MAJOR = 9,
1000
  /**
1001
   * The minor version (major.MINOR.revision) of simdutf being used.
1002
   */
1003
  SIMDUTF_VERSION_MINOR = 3,
1004
  /**
1005
   * The revision (major.minor.REVISION) of simdutf being used.
1006
   */
1007
  SIMDUTF_VERSION_REVISION = 3
1008
};
1009
} // namespace simdutf
1010
1011
#endif // SIMDUTF_SIMDUTF_VERSION_H
1012
/* end file include/simdutf/simdutf_version.h */
1013
/* begin file include/simdutf/implementation.h */
1014
#ifndef SIMDUTF_IMPLEMENTATION_H
1015
#define SIMDUTF_IMPLEMENTATION_H
1016
#if !defined(SIMDUTF_NO_THREADS)
1017
  #include <atomic>
1018
#endif
1019
#ifdef SIMDUTF_INTERNAL_TESTS
1020
  #include <vector>
1021
#endif
1022
/* begin file include/simdutf/internal/isadetection.h */
1023
/* From
1024
https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h
1025
Highly modified.
1026
1027
Copyright (c) 2016-     Facebook, Inc            (Adam Paszke)
1028
Copyright (c) 2014-     Facebook, Inc            (Soumith Chintala)
1029
Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
1030
Copyright (c) 2012-2014 Deepmind Technologies    (Koray Kavukcuoglu)
1031
Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
1032
Copyright (c) 2011-2013 NYU                      (Clement Farabet)
1033
Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou,
1034
Iain Melvin, Jason Weston) Copyright (c) 2006      Idiap Research Institute
1035
(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,
1036
Samy Bengio, Johnny Mariethoz)
1037
1038
All rights reserved.
1039
1040
Redistribution and use in source and binary forms, with or without
1041
modification, are permitted provided that the following conditions are met:
1042
1043
1. Redistributions of source code must retain the above copyright
1044
   notice, this list of conditions and the following disclaimer.
1045
1046
2. Redistributions in binary form must reproduce the above copyright
1047
   notice, this list of conditions and the following disclaimer in the
1048
   documentation and/or other materials provided with the distribution.
1049
1050
3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories
1051
America and IDIAP Research Institute nor the names of its contributors may be
1052
   used to endorse or promote products derived from this software without
1053
   specific prior written permission.
1054
1055
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
1056
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1057
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1058
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
1059
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
1060
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
1061
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
1062
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
1063
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
1064
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
1065
POSSIBILITY OF SUCH DAMAGE.
1066
*/
1067
1068
#ifndef SIMDutf_INTERNAL_ISADETECTION_H
1069
#define SIMDutf_INTERNAL_ISADETECTION_H
1070
1071
#include <cstdint>
1072
#include <cstdlib>
1073
#if defined(_MSC_VER)
1074
  #include <intrin.h>
1075
#elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) ||           \
1076
    defined(__FILC__)
1077
  #include <cpuid.h>
1078
#endif
1079
1080
#ifdef __FILC__
1081
  #include <stdfil.h>
1082
#endif
1083
1084
1085
// RISC-V ISA detection utilities
1086
#if SIMDUTF_IS_RISCV64 && defined(__linux__)
1087
  #include <unistd.h> // for syscall
1088
// We define these ourselves, for backwards compatibility
1089
struct simdutf_riscv_hwprobe {
1090
  int64_t key;
1091
  uint64_t value;
1092
};
1093
  #define simdutf_riscv_hwprobe(...) syscall(258, __VA_ARGS__)
1094
  #define SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0 4
1095
  #define SIMDUTF_RISCV_HWPROBE_IMA_V (1 << 2)
1096
  #define SIMDUTF_RISCV_HWPROBE_EXT_ZVBB (1 << 17)
1097
#endif // SIMDUTF_IS_RISCV64 && defined(__linux__)
1098
1099
#if defined(__loongarch__) && defined(__linux__)
1100
  #include <sys/auxv.h>
1101
// bits/hwcap.h
1102
// #define HWCAP_LOONGARCH_LSX             (1 << 4)
1103
// #define HWCAP_LOONGARCH_LASX            (1 << 5)
1104
#endif
1105
#if defined(__aarch64__) && defined(__linux__)
1106
  #include <sys/auxv.h>
1107
#endif
1108
#if (defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)) &&      \
1109
    defined(_WIN32) && !defined(_WINDOWS_)
1110
// We avoid including <windows.h> (macro pollution); this matches the
1111
// declaration in the Windows SDK (BOOL WINAPI
1112
// IsProcessorFeaturePresent(DWORD)).
1113
extern "C" __declspec(dllimport) int __stdcall IsProcessorFeaturePresent(
1114
    unsigned long ProcessorFeature);
1115
#endif
1116
1117
namespace simdutf {
1118
namespace internal {
1119
1120
enum instruction_set {
1121
  DEFAULT = 0x0,
1122
  NEON = 0x1,
1123
  AVX2 = 0x4,
1124
  SSE42 = 0x8,
1125
  PCLMULQDQ = 0x10,
1126
  BMI1 = 0x20,
1127
  BMI2 = 0x40,
1128
  ALTIVEC = 0x80,
1129
  AVX512F = 0x100,
1130
  AVX512DQ = 0x200,
1131
  AVX512IFMA = 0x400,
1132
  AVX512PF = 0x800,
1133
  AVX512ER = 0x1000,
1134
  AVX512CD = 0x2000,
1135
  AVX512BW = 0x4000,
1136
  AVX512VL = 0x8000,
1137
  AVX512VBMI2 = 0x10000,
1138
  AVX512VPOPCNTDQ = 0x2000,
1139
  RVV = 0x4000,
1140
  ZVBB = 0x8000,
1141
  LSX = 0x40000,
1142
  LASX = 0x80000,
1143
  SVE = 0x100000,
1144
  SVE2 = 0x200000,
1145
};
1146
1147
#if defined(__PPC64__)
1148
1149
static inline uint32_t detect_supported_architectures() {
1150
  return instruction_set::ALTIVEC;
1151
}
1152
1153
#elif SIMDUTF_IS_RISCV64
1154
1155
static inline uint32_t detect_supported_architectures() {
1156
  uint32_t host_isa = instruction_set::DEFAULT;
1157
  #if SIMDUTF_IS_RVV
1158
  host_isa |= instruction_set::RVV;
1159
  #endif
1160
  #if SIMDUTF_IS_ZVBB
1161
  host_isa |= instruction_set::ZVBB;
1162
  #endif
1163
  #if defined(__linux__)
1164
  simdutf_riscv_hwprobe probes[] = {{SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0, 0}};
1165
  long ret = simdutf_riscv_hwprobe(&probes, sizeof probes / sizeof *probes, 0,
1166
                                   nullptr, 0);
1167
  if (ret == 0) {
1168
    uint64_t extensions = probes[0].value;
1169
    if (extensions & SIMDUTF_RISCV_HWPROBE_IMA_V)
1170
      host_isa |= instruction_set::RVV;
1171
    if (extensions & SIMDUTF_RISCV_HWPROBE_EXT_ZVBB)
1172
      host_isa |= instruction_set::ZVBB;
1173
  }
1174
  #endif
1175
  #if defined(RUN_IN_SPIKE_SIMULATOR)
1176
  // Proxy Kernel does not implement yet hwprobe syscall
1177
  host_isa |= instruction_set::RVV;
1178
  #endif
1179
  return host_isa;
1180
}
1181
1182
#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
1183
1184
  #if defined(__linux__)
1185
    // The kernel advertises SVE in AT_HWCAP and SVE2 in AT_HWCAP2. Older
1186
    // headers may not define these constants, so we provide the kernel's values
1187
    // (we deliberately do not include <asm/hwcap.h>, which is not available on
1188
    // all toolchains, e.g., musl without linux-headers).
1189
    #ifndef AT_HWCAP2
1190
      #define AT_HWCAP2 26
1191
    #endif
1192
    #ifndef HWCAP_SVE
1193
      #define HWCAP_SVE (1 << 22)
1194
    #endif
1195
    #ifndef HWCAP2_SVE2
1196
      #define HWCAP2_SVE2 (1 << 1)
1197
    #endif
1198
  #endif // __linux__
1199
1200
  #if defined(_WIN32)
1201
    // Only recent Windows SDKs define these processor features.
1202
    #ifndef PF_ARM_SVE_INSTRUCTIONS_AVAILABLE
1203
      #define PF_ARM_SVE_INSTRUCTIONS_AVAILABLE 46
1204
    #endif
1205
    #ifndef PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE
1206
      #define PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE 47
1207
    #endif
1208
  #endif // _WIN32
1209
1210
static inline uint32_t detect_supported_architectures() {
1211
  // NEON is mandatory on AArch64.
1212
  uint32_t host_isa = instruction_set::NEON;
1213
  #if defined(__linux__)
1214
  unsigned long hwcap = getauxval(AT_HWCAP);
1215
  unsigned long hwcap2 = getauxval(AT_HWCAP2);
1216
  if (hwcap & HWCAP_SVE) {
1217
    host_isa |= instruction_set::SVE;
1218
    // We only claim SVE2 when SVE is also present. Before Linux 6.14, the
1219
    // kernel set HWCAP2_SVE2 on processors implementing SME(2) but not SVE,
1220
    // because SVE2 instructions are available in streaming mode. Our SVE2
1221
    // code runs in non-streaming mode and needs actual SVE.
1222
    if (hwcap2 & HWCAP2_SVE2) {
1223
      host_isa |= instruction_set::SVE2;
1224
    }
1225
  }
1226
  #elif defined(_WIN32)
1227
  if (IsProcessorFeaturePresent(PF_ARM_SVE_INSTRUCTIONS_AVAILABLE)) {
1228
    host_isa |= instruction_set::SVE;
1229
    // As on Linux, require SVE before claiming SVE2.
1230
    if (IsProcessorFeaturePresent(PF_ARM_SVE2_INSTRUCTIONS_AVAILABLE)) {
1231
      host_isa |= instruction_set::SVE2;
1232
    }
1233
  }
1234
  #endif
1235
  // On other systems (e.g., macOS, where Apple Silicon has no SVE), we only
1236
  // report NEON.
1237
  return host_isa;
1238
}
1239
1240
#elif defined(__x86_64__) || defined(_M_AMD64) // x64
1241
1242
namespace {
1243
namespace cpuid_bit {
1244
// Can be found on Intel ISA Reference for CPUID
1245
1246
// EAX = 0x01
1247
constexpr uint32_t pclmulqdq = uint32_t(1)
1248
                               << 1; ///< @private bit  1 of ECX for EAX=0x1
1249
constexpr uint32_t sse42 = uint32_t(1)
1250
                           << 20; ///< @private bit 20 of ECX for EAX=0x1
1251
constexpr uint32_t osxsave =
1252
    (uint32_t(1) << 26) |
1253
    (uint32_t(1) << 27); ///< @private bits 26+27 of ECX for EAX=0x1
1254
1255
// EAX = 0x7f (Structured Extended Feature Flags), ECX = 0x00 (Sub-leaf)
1256
// See: "Table 3-8. Information Returned by CPUID Instruction"
1257
namespace ebx {
1258
constexpr uint32_t bmi1 = uint32_t(1) << 3;
1259
constexpr uint32_t avx2 = uint32_t(1) << 5;
1260
constexpr uint32_t bmi2 = uint32_t(1) << 8;
1261
constexpr uint32_t avx512f = uint32_t(1) << 16;
1262
constexpr uint32_t avx512dq = uint32_t(1) << 17;
1263
constexpr uint32_t avx512ifma = uint32_t(1) << 21;
1264
constexpr uint32_t avx512cd = uint32_t(1) << 28;
1265
constexpr uint32_t avx512bw = uint32_t(1) << 30;
1266
constexpr uint32_t avx512vl = uint32_t(1) << 31;
1267
} // namespace ebx
1268
1269
namespace ecx {
1270
constexpr uint32_t avx512vbmi = uint32_t(1) << 1;
1271
constexpr uint32_t avx512vbmi2 = uint32_t(1) << 6;
1272
constexpr uint32_t avx512vnni = uint32_t(1) << 11;
1273
constexpr uint32_t avx512bitalg = uint32_t(1) << 12;
1274
constexpr uint32_t avx512vpopcnt = uint32_t(1) << 14;
1275
} // namespace ecx
1276
namespace edx {
1277
constexpr uint32_t avx512vp2intersect = uint32_t(1) << 8;
1278
}
1279
namespace xcr0_bit {
1280
constexpr uint64_t avx256_saved = uint64_t(1) << 2; ///< @private bit 2 = AVX
1281
constexpr uint64_t avx512_saved =
1282
    uint64_t(7) << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM
1283
} // namespace xcr0_bit
1284
} // namespace cpuid_bit
1285
} // namespace
1286
1287
static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
1288
2
                         uint32_t *edx) {
1289
  #if defined(_MSC_VER)
1290
  int cpu_info[4];
1291
  __cpuidex(cpu_info, *eax, *ecx);
1292
  *eax = cpu_info[0];
1293
  *ebx = cpu_info[1];
1294
  *ecx = cpu_info[2];
1295
  *edx = cpu_info[3];
1296
  #elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) ||         \
1297
      defined(__FILC__)
1298
  uint32_t level = *eax;
1299
  __get_cpuid(level, eax, ebx, ecx, edx);
1300
  #else
1301
2
  uint32_t a = *eax, b, c = *ecx, d;
1302
2
  asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
1303
2
  *eax = a;
1304
2
  *ebx = b;
1305
2
  *ecx = c;
1306
2
  *edx = d;
1307
2
  #endif
1308
2
}
Unexecuted instantiation: parser.cpp:simdutf::internal::cpuid(unsigned int*, unsigned int*, unsigned int*, unsigned int*)
Unexecuted instantiation: base64.cpp:simdutf::internal::cpuid(unsigned int*, unsigned int*, unsigned int*, unsigned int*)
simdutf.cpp:simdutf::internal::cpuid(unsigned int*, unsigned int*, unsigned int*, unsigned int*)
Line
Count
Source
1288
2
                         uint32_t *edx) {
1289
  #if defined(_MSC_VER)
1290
  int cpu_info[4];
1291
  __cpuidex(cpu_info, *eax, *ecx);
1292
  *eax = cpu_info[0];
1293
  *ebx = cpu_info[1];
1294
  *ecx = cpu_info[2];
1295
  *edx = cpu_info[3];
1296
  #elif (defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)) ||         \
1297
      defined(__FILC__)
1298
  uint32_t level = *eax;
1299
  __get_cpuid(level, eax, ebx, ecx, edx);
1300
  #else
1301
2
  uint32_t a = *eax, b, c = *ecx, d;
1302
2
  asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
1303
2
  *eax = a;
1304
2
  *ebx = b;
1305
2
  *ecx = c;
1306
2
  *edx = d;
1307
2
  #endif
1308
2
}
1309
1310
1
static inline uint64_t xgetbv() {
1311
  #if defined(_MSC_VER)
1312
  return _xgetbv(0);
1313
  #elif defined(__FILC__)
1314
  return zxgetbv();
1315
  #else
1316
1
  uint32_t xcr0_lo, xcr0_hi;
1317
1
  asm volatile("xgetbv\n\t" : "=a"(xcr0_lo), "=d"(xcr0_hi) : "c"(0));
1318
1
  return xcr0_lo | ((uint64_t)xcr0_hi << 32);
1319
1
  #endif
1320
1
}
Unexecuted instantiation: parser.cpp:simdutf::internal::xgetbv()
Unexecuted instantiation: base64.cpp:simdutf::internal::xgetbv()
simdutf.cpp:simdutf::internal::xgetbv()
Line
Count
Source
1310
1
static inline uint64_t xgetbv() {
1311
  #if defined(_MSC_VER)
1312
  return _xgetbv(0);
1313
  #elif defined(__FILC__)
1314
  return zxgetbv();
1315
  #else
1316
1
  uint32_t xcr0_lo, xcr0_hi;
1317
1
  asm volatile("xgetbv\n\t" : "=a"(xcr0_lo), "=d"(xcr0_hi) : "c"(0));
1318
1
  return xcr0_lo | ((uint64_t)xcr0_hi << 32);
1319
1
  #endif
1320
1
}
1321
1322
1
static inline uint32_t detect_supported_architectures() {
1323
1
  uint32_t eax;
1324
1
  uint32_t ebx = 0;
1325
1
  uint32_t ecx = 0;
1326
1
  uint32_t edx = 0;
1327
1
  uint32_t host_isa = 0x0;
1328
1329
  // EBX for EAX=0x1
1330
1
  eax = 0x1;
1331
1
  cpuid(&eax, &ebx, &ecx, &edx);
1332
1333
1
  if (ecx & cpuid_bit::sse42) {
1334
1
    host_isa |= instruction_set::SSE42;
1335
1
  }
1336
1337
1
  if (ecx & cpuid_bit::pclmulqdq) {
1338
1
    host_isa |= instruction_set::PCLMULQDQ;
1339
1
  }
1340
1341
1
  if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
1342
0
    return host_isa;
1343
0
  }
1344
1345
  // xgetbv for checking if the OS saves registers
1346
1
  uint64_t xcr0 = xgetbv();
1347
1348
1
  if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
1349
0
    return host_isa;
1350
0
  }
1351
  // ECX for EAX=0x7
1352
1
  eax = 0x7;
1353
1
  ecx = 0x0; // Sub-leaf = 0
1354
1
  cpuid(&eax, &ebx, &ecx, &edx);
1355
1
  if (ebx & cpuid_bit::ebx::avx2) {
1356
1
    host_isa |= instruction_set::AVX2;
1357
1
  }
1358
1
  if (ebx & cpuid_bit::ebx::bmi1) {
1359
1
    host_isa |= instruction_set::BMI1;
1360
1
  }
1361
1
  if (ebx & cpuid_bit::ebx::bmi2) {
1362
1
    host_isa |= instruction_set::BMI2;
1363
1
  }
1364
1
  if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) ==
1365
1
        cpuid_bit::xcr0_bit::avx512_saved)) {
1366
1
    return host_isa;
1367
1
  }
1368
0
  if (ebx & cpuid_bit::ebx::avx512f) {
1369
0
    host_isa |= instruction_set::AVX512F;
1370
0
  }
1371
0
  if (ebx & cpuid_bit::ebx::avx512bw) {
1372
0
    host_isa |= instruction_set::AVX512BW;
1373
0
  }
1374
0
  if (ebx & cpuid_bit::ebx::avx512cd) {
1375
0
    host_isa |= instruction_set::AVX512CD;
1376
0
  }
1377
0
  if (ebx & cpuid_bit::ebx::avx512dq) {
1378
0
    host_isa |= instruction_set::AVX512DQ;
1379
0
  }
1380
0
  if (ebx & cpuid_bit::ebx::avx512vl) {
1381
0
    host_isa |= instruction_set::AVX512VL;
1382
0
  }
1383
0
  if (ecx & cpuid_bit::ecx::avx512vbmi2) {
1384
0
    host_isa |= instruction_set::AVX512VBMI2;
1385
0
  }
1386
0
  if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
1387
0
    host_isa |= instruction_set::AVX512VPOPCNTDQ;
1388
0
  }
1389
0
  return host_isa;
1390
1
}
Unexecuted instantiation: parser.cpp:simdutf::internal::detect_supported_architectures()
Unexecuted instantiation: base64.cpp:simdutf::internal::detect_supported_architectures()
simdutf.cpp:simdutf::internal::detect_supported_architectures()
Line
Count
Source
1322
1
static inline uint32_t detect_supported_architectures() {
1323
1
  uint32_t eax;
1324
1
  uint32_t ebx = 0;
1325
1
  uint32_t ecx = 0;
1326
1
  uint32_t edx = 0;
1327
1
  uint32_t host_isa = 0x0;
1328
1329
  // EBX for EAX=0x1
1330
1
  eax = 0x1;
1331
1
  cpuid(&eax, &ebx, &ecx, &edx);
1332
1333
1
  if (ecx & cpuid_bit::sse42) {
1334
1
    host_isa |= instruction_set::SSE42;
1335
1
  }
1336
1337
1
  if (ecx & cpuid_bit::pclmulqdq) {
1338
1
    host_isa |= instruction_set::PCLMULQDQ;
1339
1
  }
1340
1341
1
  if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
1342
0
    return host_isa;
1343
0
  }
1344
1345
  // xgetbv for checking if the OS saves registers
1346
1
  uint64_t xcr0 = xgetbv();
1347
1348
1
  if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
1349
0
    return host_isa;
1350
0
  }
1351
  // ECX for EAX=0x7
1352
1
  eax = 0x7;
1353
1
  ecx = 0x0; // Sub-leaf = 0
1354
1
  cpuid(&eax, &ebx, &ecx, &edx);
1355
1
  if (ebx & cpuid_bit::ebx::avx2) {
1356
1
    host_isa |= instruction_set::AVX2;
1357
1
  }
1358
1
  if (ebx & cpuid_bit::ebx::bmi1) {
1359
1
    host_isa |= instruction_set::BMI1;
1360
1
  }
1361
1
  if (ebx & cpuid_bit::ebx::bmi2) {
1362
1
    host_isa |= instruction_set::BMI2;
1363
1
  }
1364
1
  if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) ==
1365
1
        cpuid_bit::xcr0_bit::avx512_saved)) {
1366
1
    return host_isa;
1367
1
  }
1368
0
  if (ebx & cpuid_bit::ebx::avx512f) {
1369
0
    host_isa |= instruction_set::AVX512F;
1370
0
  }
1371
0
  if (ebx & cpuid_bit::ebx::avx512bw) {
1372
0
    host_isa |= instruction_set::AVX512BW;
1373
0
  }
1374
0
  if (ebx & cpuid_bit::ebx::avx512cd) {
1375
0
    host_isa |= instruction_set::AVX512CD;
1376
0
  }
1377
0
  if (ebx & cpuid_bit::ebx::avx512dq) {
1378
0
    host_isa |= instruction_set::AVX512DQ;
1379
0
  }
1380
0
  if (ebx & cpuid_bit::ebx::avx512vl) {
1381
0
    host_isa |= instruction_set::AVX512VL;
1382
0
  }
1383
0
  if (ecx & cpuid_bit::ecx::avx512vbmi2) {
1384
0
    host_isa |= instruction_set::AVX512VBMI2;
1385
0
  }
1386
0
  if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
1387
0
    host_isa |= instruction_set::AVX512VPOPCNTDQ;
1388
0
  }
1389
0
  return host_isa;
1390
1
}
1391
#elif defined(__loongarch__)
1392
1393
static inline uint32_t detect_supported_architectures() {
1394
  uint32_t host_isa = instruction_set::DEFAULT;
1395
  #if defined(__linux__)
1396
  uint64_t hwcap = 0;
1397
  hwcap = getauxval(AT_HWCAP);
1398
  if (hwcap & HWCAP_LOONGARCH_LSX) {
1399
    host_isa |= instruction_set::LSX;
1400
  }
1401
  if (hwcap & HWCAP_LOONGARCH_LASX) {
1402
    host_isa |= instruction_set::LASX;
1403
  }
1404
  #endif
1405
  return host_isa;
1406
}
1407
#else // fallback
1408
1409
// includes 32-bit ARM.
1410
static inline uint32_t detect_supported_architectures() {
1411
  return instruction_set::DEFAULT;
1412
}
1413
1414
#endif // end SIMD extension detection code
1415
1416
} // namespace internal
1417
} // namespace simdutf
1418
1419
#endif // SIMDutf_INTERNAL_ISADETECTION_H
1420
/* end file include/simdutf/internal/isadetection.h */
1421
1422
#include <string_view>
1423
#if SIMDUTF_SPAN
1424
  #include <concepts>
1425
  #include <type_traits>
1426
  #include <span>
1427
  #include <tuple>
1428
  #include <utility> // for std::unreachable
1429
#endif
1430
// The following defines are conditionally enabled/disabled during amalgamation.
1431
// By default all features are enabled, regular code shouldn't check them. Only
1432
// when user code really relies of a selected subset, it's good to verify these
1433
// flags, like:
1434
//
1435
//      #if !SIMDUTF_FEATURE_UTF16
1436
//      #   error("Please amalgamate simdutf with UTF-16 support")
1437
//      #endif
1438
//
1439
#ifndef SIMDUTF_FEATURE_DETECT_ENCODING
1440
  #define SIMDUTF_FEATURE_DETECT_ENCODING 1
1441
#endif
1442
#ifndef SIMDUTF_FEATURE_ASCII
1443
  #define SIMDUTF_FEATURE_ASCII 1
1444
#endif
1445
#ifndef SIMDUTF_FEATURE_LATIN1
1446
  #define SIMDUTF_FEATURE_LATIN1 1
1447
#endif
1448
#ifndef SIMDUTF_FEATURE_UTF8
1449
  #define SIMDUTF_FEATURE_UTF8 1
1450
#endif
1451
#ifndef SIMDUTF_FEATURE_UTF16
1452
  #define SIMDUTF_FEATURE_UTF16 1
1453
#endif
1454
#ifndef SIMDUTF_FEATURE_UTF32
1455
  #define SIMDUTF_FEATURE_UTF32 1
1456
#endif
1457
#ifndef SIMDUTF_FEATURE_BASE64
1458
  #define SIMDUTF_FEATURE_BASE64 1
1459
#endif
1460
1461
/// helpers placed in namespace detail are not a part of the public API
1462
namespace simdutf {
1463
namespace detail {
1464
namespace {
1465
// this is to avoid including <algorithm> just for min
1466
0
constexpr std::size_t min(std::size_t a, std::size_t b) {
1467
0
  return a < b ? a : b;
1468
0
}
Unexecuted instantiation: parser.cpp:simdutf::detail::(anonymous namespace)::min(unsigned long, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::detail::(anonymous namespace)::min(unsigned long, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::detail::(anonymous namespace)::min(unsigned long, unsigned long)
1469
template <typename T, typename U>
1470
constexpr std::size_t min(const T &a, const U &b) = delete;
1471
} // namespace
1472
} // namespace detail
1473
} // namespace simdutf
1474
1475
#if SIMDUTF_CPLUSPLUS23
1476
/* begin file include/simdutf/constexpr_ptr.h */
1477
#ifndef SIMDUTF_CONSTEXPR_PTR_H
1478
#define SIMDUTF_CONSTEXPR_PTR_H
1479
1480
#include <cstddef>
1481
1482
namespace simdutf {
1483
namespace detail {
1484
/**
1485
 * The constexpr_ptr class is a workaround for reinterpret_cast not being
1486
 * allowed during constant evaluation.
1487
 */
1488
template <typename to, typename from>
1489
  requires(sizeof(to) == sizeof(from))
1490
struct constexpr_ptr {
1491
  const from *p;
1492
1493
  constexpr explicit constexpr_ptr(const from *ptr) noexcept : p(ptr) {}
1494
1495
  constexpr to operator*() const noexcept { return static_cast<to>(*p); }
1496
1497
  constexpr constexpr_ptr &operator++() noexcept {
1498
    ++p;
1499
    return *this;
1500
  }
1501
1502
  constexpr constexpr_ptr operator++(int) noexcept {
1503
    auto old = *this;
1504
    ++p;
1505
    return old;
1506
  }
1507
1508
  constexpr constexpr_ptr &operator--() noexcept {
1509
    --p;
1510
    return *this;
1511
  }
1512
1513
  constexpr constexpr_ptr operator--(int) noexcept {
1514
    auto old = *this;
1515
    --p;
1516
    return old;
1517
  }
1518
1519
  constexpr constexpr_ptr &operator+=(std::ptrdiff_t n) noexcept {
1520
    p += n;
1521
    return *this;
1522
  }
1523
1524
  constexpr constexpr_ptr &operator-=(std::ptrdiff_t n) noexcept {
1525
    p -= n;
1526
    return *this;
1527
  }
1528
1529
  constexpr constexpr_ptr operator+(std::ptrdiff_t n) const noexcept {
1530
    return constexpr_ptr{p + n};
1531
  }
1532
1533
  constexpr constexpr_ptr operator-(std::ptrdiff_t n) const noexcept {
1534
    return constexpr_ptr{p - n};
1535
  }
1536
1537
  constexpr std::ptrdiff_t operator-(const constexpr_ptr &o) const noexcept {
1538
    return p - o.p;
1539
  }
1540
1541
  constexpr to operator[](std::ptrdiff_t n) const noexcept {
1542
    return static_cast<to>(*(p + n));
1543
  }
1544
1545
  // to prevent compilation errors for memcpy, even if it is never
1546
  // called during constant evaluation
1547
  constexpr operator const void *() const noexcept { return p; }
1548
};
1549
1550
template <typename to, typename from>
1551
constexpr constexpr_ptr<to, from> constexpr_cast_ptr(from *p) noexcept {
1552
  return constexpr_ptr<to, from>{p};
1553
}
1554
1555
/**
1556
 * helper type for constexpr_writeptr, so it is possible to
1557
 * do "*ptr = val;"
1558
 */
1559
template <typename SrcType, typename TargetType>
1560
struct constexpr_write_ptr_proxy {
1561
1562
  constexpr explicit constexpr_write_ptr_proxy(TargetType *raw) : p(raw) {}
1563
1564
  constexpr constexpr_write_ptr_proxy &operator=(SrcType v) {
1565
    *p = static_cast<TargetType>(v);
1566
    return *this;
1567
  }
1568
1569
  TargetType *p;
1570
};
1571
1572
/**
1573
 * helper for working around reinterpret_cast not being allowed during constexpr
1574
 * evaluation. will try to act as a SrcType* but actually write to the pointer
1575
 * given in the constructor, which is of another type TargetType
1576
 */
1577
template <typename SrcType, typename TargetType> struct constexpr_write_ptr {
1578
  constexpr explicit constexpr_write_ptr(TargetType *raw) : p(raw) {}
1579
1580
  constexpr constexpr_write_ptr_proxy<SrcType, TargetType> operator*() const {
1581
    return constexpr_write_ptr_proxy<SrcType, TargetType>{p};
1582
  }
1583
1584
  constexpr constexpr_write_ptr_proxy<SrcType, TargetType>
1585
  operator[](std::ptrdiff_t n) const {
1586
    return constexpr_write_ptr_proxy<SrcType, TargetType>{p + n};
1587
  }
1588
1589
  constexpr constexpr_write_ptr &operator++() {
1590
    ++p;
1591
    return *this;
1592
  }
1593
1594
  constexpr constexpr_write_ptr operator++(int) {
1595
    constexpr_write_ptr old = *this;
1596
    ++p;
1597
    return old;
1598
  }
1599
1600
  constexpr std::ptrdiff_t operator-(const constexpr_write_ptr &other) const {
1601
    return p - other.p;
1602
  }
1603
1604
  TargetType *p;
1605
};
1606
1607
template <typename SrcType, typename TargetType>
1608
constexpr auto constexpr_cast_writeptr(TargetType *raw) {
1609
  return constexpr_write_ptr<SrcType, TargetType>{raw};
1610
}
1611
1612
} // namespace detail
1613
} // namespace simdutf
1614
#endif
1615
/* end file include/simdutf/constexpr_ptr.h */
1616
#endif
1617
1618
#if SIMDUTF_SPAN
1619
/// helpers placed in namespace detail are not a part of the public API
1620
namespace simdutf {
1621
namespace detail {
1622
/**
1623
 * matches a byte, in the many ways C++ allows. note that these
1624
 * are all distinct types.
1625
 */
1626
template <typename T>
1627
concept byte_like = std::is_same_v<T, std::byte> ||     //
1628
                    std::is_same_v<T, char> ||          //
1629
                    std::is_same_v<T, signed char> ||   //
1630
                    std::is_same_v<T, unsigned char> || //
1631
                    std::is_same_v<T, char8_t>;
1632
1633
template <typename T>
1634
concept is_byte_like = byte_like<std::remove_cvref_t<T>>;
1635
1636
template <typename T>
1637
concept is_pointer = std::is_pointer_v<T>;
1638
1639
/**
1640
 * matches anything that behaves like std::span and points to character-like
1641
 * data such as: std::byte, char, unsigned char, signed char, std::int8_t,
1642
 * std::uint8_t
1643
 */
1644
template <typename T>
1645
concept input_span_of_byte_like = requires(const T &t) {
1646
  { t.size() } noexcept -> std::convertible_to<std::size_t>;
1647
  { t.data() } noexcept -> is_pointer;
1648
  { *t.data() } noexcept -> is_byte_like;
1649
};
1650
1651
template <typename T>
1652
concept is_mutable = !std::is_const_v<std::remove_reference_t<T>>;
1653
1654
/**
1655
 * like span_of_byte_like, but for an output span (intended to be written to)
1656
 */
1657
template <typename T>
1658
concept output_span_of_byte_like = requires(T &t) {
1659
  { t.size() } noexcept -> std::convertible_to<std::size_t>;
1660
  { t.data() } noexcept -> is_pointer;
1661
  { *t.data() } noexcept -> is_byte_like;
1662
  { *t.data() } noexcept -> is_mutable;
1663
};
1664
1665
/**
1666
 * a pointer like object, when indexed, results in a byte like result.
1667
 * valid examples: char*, const char*, std::array<char,10>
1668
 * invalid examples: int*, std::array<int,10>
1669
 */
1670
template <class InputPtr>
1671
concept indexes_into_byte_like = requires(InputPtr p) {
1672
  { std::decay_t<decltype(p[0])>{} } -> simdutf::detail::byte_like;
1673
};
1674
template <class InputPtr>
1675
concept indexes_into_utf16 = requires(InputPtr p) {
1676
  { std::decay_t<decltype(p[0])>{} } -> std::same_as<char16_t>;
1677
};
1678
template <class InputPtr>
1679
concept indexes_into_utf32 = requires(InputPtr p) {
1680
  { std::decay_t<decltype(p[0])>{} } -> std::same_as<char32_t>;
1681
};
1682
1683
template <class InputPtr>
1684
concept index_assignable_from_char = requires(InputPtr p, char s) {
1685
  { p[0] = s };
1686
};
1687
1688
/**
1689
 * a pointer like object that results in a uint32_t when indexed.
1690
 * valid examples: uint32_t*
1691
 */
1692
template <class InputPtr>
1693
concept indexes_into_uint32 = requires(InputPtr p) {
1694
  { std::decay_t<decltype(p[0])>{} } -> std::same_as<std::uint32_t>;
1695
};
1696
} // namespace detail
1697
} // namespace simdutf
1698
#endif // SIMDUTF_SPAN
1699
1700
// these includes are needed for constexpr support. they are
1701
// not part of the public api.
1702
/* begin file include/simdutf/scalar/swap_bytes.h */
1703
#ifndef SIMDUTF_SWAP_BYTES_H
1704
#define SIMDUTF_SWAP_BYTES_H
1705
1706
namespace simdutf {
1707
namespace scalar {
1708
1709
constexpr inline simdutf_warn_unused uint16_t
1710
0
u16_swap_bytes(const uint16_t word) {
1711
0
  return uint16_t((word >> 8) | (word << 8));
1712
0
}
1713
1714
constexpr inline simdutf_warn_unused uint32_t
1715
995
u32_swap_bytes(const uint32_t word) {
1716
995
  return ((word >> 24) & 0xff) |      // move byte 3 to byte 0
1717
995
         ((word << 8) & 0xff0000) |   // move byte 1 to byte 2
1718
995
         ((word >> 8) & 0xff00) |     // move byte 2 to byte 1
1719
995
         ((word << 24) & 0xff000000); // byte 0 to byte 3
1720
995
}
1721
1722
namespace utf32 {
1723
template <endianness big_endian> constexpr uint32_t swap_if_needed(uint32_t c) {
1724
  return !match_system(big_endian) ? scalar::u32_swap_bytes(c) : c;
1725
}
1726
} // namespace utf32
1727
1728
namespace utf16 {
1729
template <endianness big_endian> constexpr uint16_t swap_if_needed(uint16_t c) {
1730
  return !match_system(big_endian) ? scalar::u16_swap_bytes(c) : c;
1731
}
1732
} // namespace utf16
1733
1734
} // namespace scalar
1735
} // namespace simdutf
1736
1737
#endif
1738
/* end file include/simdutf/scalar/swap_bytes.h */
1739
/* begin file include/simdutf/scalar/ascii.h */
1740
#ifndef SIMDUTF_ASCII_H
1741
#define SIMDUTF_ASCII_H
1742
1743
#include <cstring>
1744
1745
namespace simdutf {
1746
namespace scalar {
1747
namespace {
1748
namespace ascii {
1749
1750
template <class InputPtr>
1751
#if SIMDUTF_CPLUSPLUS20
1752
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
1753
#endif
1754
simdutf_warn_unused simdutf_constexpr23 bool validate(InputPtr data,
1755
                                                      size_t len) noexcept {
1756
  uint64_t pos = 0;
1757
1758
#if SIMDUTF_CPLUSPLUS23
1759
  // avoid memcpy during constant evaluation
1760
  if !consteval
1761
#endif
1762
  // process in blocks of 16 bytes when possible
1763
  {
1764
    for (; pos + 16 <= len; pos += 16) {
1765
      uint64_t v1;
1766
      std::memcpy(&v1, data + pos, sizeof(uint64_t));
1767
      uint64_t v2;
1768
      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
1769
      uint64_t v{v1 | v2};
1770
      if ((v & 0x8080808080808080) != 0) {
1771
        return false;
1772
      }
1773
    }
1774
  }
1775
1776
  // process the tail byte-by-byte
1777
  for (; pos < len; pos++) {
1778
    if (static_cast<std::uint8_t>(data[pos]) >= 0b10000000) {
1779
      return false;
1780
    }
1781
  }
1782
  return true;
1783
}
1784
template <class InputPtr>
1785
#if SIMDUTF_CPLUSPLUS20
1786
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
1787
#endif
1788
simdutf_warn_unused simdutf_constexpr23 result
1789
validate_with_errors(InputPtr data, size_t len) noexcept {
1790
  size_t pos = 0;
1791
#if SIMDUTF_CPLUSPLUS23
1792
  // avoid memcpy during constant evaluation
1793
  if !consteval
1794
#endif
1795
  {
1796
    // process in blocks of 16 bytes when possible
1797
    for (; pos + 16 <= len; pos += 16) {
1798
      uint64_t v1;
1799
      std::memcpy(&v1, data + pos, sizeof(uint64_t));
1800
      uint64_t v2;
1801
      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
1802
      uint64_t v{v1 | v2};
1803
      if ((v & 0x8080808080808080) != 0) {
1804
        for (; pos < len; pos++) {
1805
          if (static_cast<std::uint8_t>(data[pos]) >= 0b10000000) {
1806
            return result(error_code::TOO_LARGE, pos);
1807
          }
1808
        }
1809
      }
1810
    }
1811
  }
1812
1813
  // process the tail byte-by-byte
1814
  for (; pos < len; pos++) {
1815
    if (static_cast<std::uint8_t>(data[pos]) >= 0b10000000) {
1816
      return result(error_code::TOO_LARGE, pos);
1817
    }
1818
  }
1819
  return result(error_code::SUCCESS, pos);
1820
}
1821
1822
} // namespace ascii
1823
} // unnamed namespace
1824
} // namespace scalar
1825
} // namespace simdutf
1826
1827
#endif
1828
/* end file include/simdutf/scalar/ascii.h */
1829
/* begin file include/simdutf/scalar/atomic_util.h */
1830
#ifndef SIMDUTF_ATOMIC_UTIL_H
1831
#define SIMDUTF_ATOMIC_UTIL_H
1832
#if SIMDUTF_ATOMIC_REF
1833
  #include <atomic>
1834
  #include <cstring>
1835
namespace simdutf {
1836
namespace scalar {
1837
1838
// This function is a memcpy that uses atomic operations to read from the
1839
// source.
1840
0
inline void memcpy_atomic_read(char *dst, const char *src, size_t len) {
1841
0
  static_assert(std::atomic_ref<char>::required_alignment == sizeof(char),
1842
0
                "std::atomic_ref requires the same alignment as char_type");
1843
  // We expect all 64-bit systems to be able to read 64-bit words from an
1844
  // aligned memory region atomically. You might be able to do better on
1845
  // specific systems, e.g., x64 systems can read 128-bit words atomically.
1846
0
  constexpr size_t alignment = sizeof(uint64_t);
1847
1848
  // Lambda for atomic byte-by-byte copy
1849
0
  auto bbb_memcpy_atomic_read = [](char *bytedst, const char *bytesrc,
1850
0
                                   size_t bytelen) noexcept {
1851
0
    char *mutable_src = const_cast<char *>(bytesrc);
1852
0
    for (size_t j = 0; j < bytelen; ++j) {
1853
0
      bytedst[j] =
1854
0
          std::atomic_ref<char>(mutable_src[j]).load(std::memory_order_relaxed);
1855
0
    }
1856
0
  };
1857
1858
  // Handle unaligned start
1859
0
  size_t offset = reinterpret_cast<std::uintptr_t>(src) % alignment;
1860
0
  if (offset) {
1861
0
    size_t to_align = detail::min(len, alignment - offset);
1862
0
    bbb_memcpy_atomic_read(dst, src, to_align);
1863
0
    src += to_align;
1864
0
    dst += to_align;
1865
0
    len -= to_align;
1866
0
  }
1867
1868
  // Process aligned 64-bit chunks
1869
0
  while (len >= alignment) {
1870
0
    auto *src_aligned = reinterpret_cast<uint64_t *>(const_cast<char *>(src));
1871
0
    const auto dst_value =
1872
0
        std::atomic_ref<uint64_t>(*src_aligned).load(std::memory_order_relaxed);
1873
0
    std::memcpy(dst, &dst_value, sizeof(uint64_t));
1874
0
    src += alignment;
1875
0
    dst += alignment;
1876
0
    len -= alignment;
1877
0
  }
1878
1879
  // Handle remaining bytes
1880
0
  if (len) {
1881
0
    bbb_memcpy_atomic_read(dst, src, len);
1882
0
  }
1883
0
}
1884
1885
// This function is a memcpy that uses atomic operations to write to the
1886
// destination.
1887
0
inline void memcpy_atomic_write(char *dst, const char *src, size_t len) {
1888
0
  static_assert(std::atomic_ref<char>::required_alignment == sizeof(char),
1889
0
                "std::atomic_ref requires the same alignment as char");
1890
  // We expect all 64-bit systems to be able to write 64-bit words to an aligned
1891
  // memory region atomically.
1892
  // You might be able to do better on specific systems, e.g., x64 systems can
1893
  // write 128-bit words atomically.
1894
0
  constexpr size_t alignment = sizeof(uint64_t);
1895
1896
  // Lambda for atomic byte-by-byte write
1897
0
  auto bbb_memcpy_atomic_write = [](char *bytedst, const char *bytesrc,
1898
0
                                    size_t bytelen) noexcept {
1899
0
    for (size_t j = 0; j < bytelen; ++j) {
1900
0
      std::atomic_ref<char>(bytedst[j])
1901
0
          .store(bytesrc[j], std::memory_order_relaxed);
1902
0
    }
1903
0
  };
1904
1905
  // Handle unaligned start
1906
0
  size_t offset = reinterpret_cast<std::uintptr_t>(dst) % alignment;
1907
0
  if (offset) {
1908
0
    size_t to_align = detail::min(len, alignment - offset);
1909
0
    bbb_memcpy_atomic_write(dst, src, to_align);
1910
0
    dst += to_align;
1911
0
    src += to_align;
1912
0
    len -= to_align;
1913
0
  }
1914
1915
  // Process aligned 64-bit chunks
1916
0
  while (len >= alignment) {
1917
0
    auto *dst_aligned = reinterpret_cast<uint64_t *>(dst);
1918
0
    uint64_t src_val;
1919
0
    std::memcpy(&src_val, src, sizeof(uint64_t)); // Non-atomic read from src
1920
0
    std::atomic_ref<uint64_t>(*dst_aligned)
1921
0
        .store(src_val, std::memory_order_relaxed);
1922
0
    dst += alignment;
1923
0
    src += alignment;
1924
0
    len -= alignment;
1925
0
  }
1926
1927
  // Handle remaining bytes
1928
0
  if (len) {
1929
0
    bbb_memcpy_atomic_write(dst, src, len);
1930
0
  }
1931
0
}
1932
} // namespace scalar
1933
} // namespace simdutf
1934
#endif // SIMDUTF_ATOMIC_REF
1935
#endif // SIMDUTF_ATOMIC_UTIL_H
1936
/* end file include/simdutf/scalar/atomic_util.h */
1937
/* begin file include/simdutf/scalar/latin1.h */
1938
#ifndef SIMDUTF_LATIN1_H
1939
#define SIMDUTF_LATIN1_H
1940
1941
namespace simdutf {
1942
namespace scalar {
1943
namespace {
1944
namespace latin1 {
1945
1946
simdutf_really_inline size_t utf8_length_from_latin1(const char *buf,
1947
0
                                                     size_t len) {
1948
0
  const uint8_t *c = reinterpret_cast<const uint8_t *>(buf);
1949
0
  size_t answer = 0;
1950
0
  for (size_t i = 0; i < len; i++) {
1951
0
    if ((c[i] >> 7)) {
1952
0
      answer++;
1953
0
    }
1954
0
  }
1955
0
  return answer + len;
1956
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::latin1::utf8_length_from_latin1(char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::latin1::utf8_length_from_latin1(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::latin1::utf8_length_from_latin1(char const*, unsigned long)
1957
1958
} // namespace latin1
1959
} // unnamed namespace
1960
} // namespace scalar
1961
} // namespace simdutf
1962
1963
#endif
1964
/* end file include/simdutf/scalar/latin1.h */
1965
/* begin file include/simdutf/scalar/latin1_to_utf16/latin1_to_utf16.h */
1966
#ifndef SIMDUTF_LATIN1_TO_UTF16_H
1967
#define SIMDUTF_LATIN1_TO_UTF16_H
1968
1969
namespace simdutf {
1970
namespace scalar {
1971
namespace {
1972
namespace latin1_to_utf16 {
1973
1974
template <endianness big_endian, typename InputPtr>
1975
#if SIMDUTF_CPLUSPLUS20
1976
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
1977
#endif
1978
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
1979
                                   char16_t *utf16_output) {
1980
  size_t pos = 0;
1981
  char16_t *start{utf16_output};
1982
1983
  while (pos < len) {
1984
    uint16_t word =
1985
        uint8_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
1986
    *utf16_output++ =
1987
        char16_t(match_system(big_endian) ? word : u16_swap_bytes(word));
1988
    pos++;
1989
  }
1990
1991
  return utf16_output - start;
1992
}
1993
1994
template <endianness big_endian>
1995
inline result convert_with_errors(const char *buf, size_t len,
1996
                                  char16_t *utf16_output) {
1997
  const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
1998
  size_t pos = 0;
1999
  char16_t *start{utf16_output};
2000
2001
  while (pos < len) {
2002
    uint16_t word =
2003
        uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
2004
    *utf16_output++ =
2005
        char16_t(match_system(big_endian) ? word : u16_swap_bytes(word));
2006
    pos++;
2007
  }
2008
2009
  return result(error_code::SUCCESS, utf16_output - start);
2010
}
2011
2012
} // namespace latin1_to_utf16
2013
} // unnamed namespace
2014
} // namespace scalar
2015
} // namespace simdutf
2016
2017
#endif
2018
/* end file include/simdutf/scalar/latin1_to_utf16/latin1_to_utf16.h */
2019
/* begin file include/simdutf/scalar/latin1_to_utf32/latin1_to_utf32.h */
2020
#ifndef SIMDUTF_LATIN1_TO_UTF32_H
2021
#define SIMDUTF_LATIN1_TO_UTF32_H
2022
2023
namespace simdutf {
2024
namespace scalar {
2025
namespace {
2026
namespace latin1_to_utf32 {
2027
2028
template <typename InputPtr>
2029
#if SIMDUTF_CPLUSPLUS20
2030
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
2031
#endif
2032
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
2033
                                   char32_t *utf32_output) {
2034
  char32_t *start{utf32_output};
2035
  for (size_t i = 0; i < len; i++) {
2036
    *utf32_output++ = uint8_t(data[i]);
2037
  }
2038
  return utf32_output - start;
2039
}
2040
2041
} // namespace latin1_to_utf32
2042
} // unnamed namespace
2043
} // namespace scalar
2044
} // namespace simdutf
2045
2046
#endif
2047
/* end file include/simdutf/scalar/latin1_to_utf32/latin1_to_utf32.h */
2048
/* begin file include/simdutf/scalar/latin1_to_utf8/latin1_to_utf8.h */
2049
#ifndef SIMDUTF_LATIN1_TO_UTF8_H
2050
#define SIMDUTF_LATIN1_TO_UTF8_H
2051
2052
#include <cstring>
2053
2054
namespace simdutf {
2055
namespace scalar {
2056
namespace {
2057
namespace latin1_to_utf8 {
2058
2059
template <typename InputPtr, typename OutputPtr>
2060
#if SIMDUTF_CPLUSPLUS20
2061
  requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
2062
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2063
#endif
2064
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
2065
0
                                   OutputPtr utf8_output) {
2066
0
  // const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
2067
0
  size_t pos = 0;
2068
0
  size_t utf8_pos = 0;
2069
0
2070
0
  while (pos < len) {
2071
0
#if SIMDUTF_CPLUSPLUS23
2072
0
    if !consteval
2073
0
#endif
2074
0
    {
2075
0
      // try to convert the next block of 16 ASCII bytes
2076
0
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
2077
0
                             // they are ascii
2078
0
        uint64_t v1;
2079
0
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
2080
0
        uint64_t v2;
2081
0
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
2082
0
        uint64_t v{v1 |
2083
0
                   v2}; // We are only interested in these bits: 1000 1000 1000
2084
0
                        // 1000, so it makes sense to concatenate everything
2085
0
        if ((v & 0x8080808080808080) ==
2086
0
            0) { // if NONE of these are set, e.g. all of them are zero, then
2087
0
                 // everything is ASCII
2088
0
          size_t final_pos = pos + 16;
2089
0
          while (pos < final_pos) {
2090
0
            utf8_output[utf8_pos++] = char(data[pos]);
2091
0
            pos++;
2092
0
          }
2093
0
          continue;
2094
0
        }
2095
0
      } // if (pos + 16 <= len)
2096
0
    } // !consteval scope
2097
0
2098
0
    unsigned char byte = data[pos];
2099
0
    if ((byte & 0x80) == 0) { // if ASCII
2100
0
      // will generate one UTF-8 bytes
2101
0
      utf8_output[utf8_pos++] = char(byte);
2102
0
      pos++;
2103
0
    } else {
2104
0
      // will generate two UTF-8 bytes
2105
0
      utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
2106
0
      utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
2107
0
      pos++;
2108
0
    }
2109
0
  } // while
2110
0
  return utf8_pos;
2111
0
}
Unexecuted instantiation: parser.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_
Unexecuted instantiation: base64.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_
Unexecuted instantiation: simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114latin1_to_utf87convertIPKhPcQaasr7simdutf6detailE22indexes_into_byte_likeIT_Esr7simdutf6detailE26index_assignable_from_charIT0_EEEmS7_mS8_
2112
2113
simdutf_really_inline size_t convert(const char *buf, size_t len,
2114
0
                                     char *utf8_output) {
2115
0
  return convert(reinterpret_cast<const unsigned char *>(buf), len,
2116
0
                 utf8_output);
2117
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert(char const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert(char const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert(char const*, unsigned long, char*)
2118
2119
inline size_t convert_safe(const char *buf, size_t len, char *utf8_output,
2120
0
                           size_t utf8_len) {
2121
0
  const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
2122
0
  size_t pos = 0;
2123
0
  size_t skip_pos = 0;
2124
0
  size_t utf8_pos = 0;
2125
0
  while (pos < len && utf8_pos < utf8_len) {
2126
0
    // try to convert the next block of 16 ASCII bytes
2127
0
    if (pos >= skip_pos && pos + 16 <= len &&
2128
0
        utf8_pos + 16 <= utf8_len) { // if it is safe to read 16 more bytes,
2129
0
                                     // check that they are ascii
2130
0
      uint64_t v1;
2131
0
      ::memcpy(&v1, data + pos, sizeof(uint64_t));
2132
0
      uint64_t v2;
2133
0
      ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
2134
0
      uint64_t v{v1 |
2135
0
                 v2}; // We are only interested in these bits: 1000 1000 1000
2136
0
                      // 1000, so it makes sense to concatenate everything
2137
0
      if ((v & 0x8080808080808080) ==
2138
0
          0) { // if NONE of these are set, e.g. all of them are zero, then
2139
0
               // everything is ASCII
2140
0
        ::memcpy(utf8_output + utf8_pos, buf + pos, 16);
2141
0
        utf8_pos += 16;
2142
0
        pos += 16;
2143
0
      } else {
2144
0
        // At least one of the next 16 bytes are not ASCII, we will process them
2145
0
        // one by one
2146
0
        skip_pos = pos + 16;
2147
0
      }
2148
0
    } else {
2149
0
      const auto byte = data[pos];
2150
0
      if ((byte & 0x80) == 0) { // if ASCII
2151
0
        // will generate one UTF-8 bytes
2152
0
        utf8_output[utf8_pos++] = char(byte);
2153
0
        pos++;
2154
0
      } else if (utf8_pos + 2 <= utf8_len) {
2155
0
        // will generate two UTF-8 bytes
2156
0
        utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
2157
0
        utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
2158
0
        pos++;
2159
0
      } else {
2160
0
        break;
2161
0
      }
2162
0
    }
2163
0
  }
2164
0
  return utf8_pos;
2165
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe(char const*, unsigned long, char*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe(char const*, unsigned long, char*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe(char const*, unsigned long, char*, unsigned long)
2166
2167
inline full_result convert_safe_with_details(const char *buf, size_t len,
2168
                                             char *utf8_output,
2169
0
                                             size_t utf8_len) {
2170
0
  const size_t output_count = convert_safe(buf, len, utf8_output, utf8_len);
2171
0
  // Recover the consumed input count from the completed output. The runtime
2172
0
  // safe converter uses this helper only for its short scalar tail.
2173
0
  size_t input_count = 0;
2174
0
  size_t counted_output = 0;
2175
0
  while (input_count < len) {
2176
0
    const size_t width =
2177
0
        uint8_t(buf[input_count]) < uint8_t(0x80) ? size_t(1) : size_t(2);
2178
0
    if (counted_output + width > output_count) {
2179
0
      break;
2180
0
    }
2181
0
    input_count++;
2182
0
    counted_output += width;
2183
0
  }
2184
0
  return full_result(input_count == len ? error_code::SUCCESS
2185
0
                                        : error_code::OUTPUT_BUFFER_TOO_SMALL,
2186
0
                     input_count, output_count);
2187
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe_with_details(char const*, unsigned long, char*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe_with_details(char const*, unsigned long, char*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::latin1_to_utf8::convert_safe_with_details(char const*, unsigned long, char*, unsigned long)
2188
2189
template <typename InputPtr, typename OutputPtr>
2190
#if SIMDUTF_CPLUSPLUS20
2191
  requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
2192
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2193
#endif
2194
simdutf_constexpr23 size_t convert_safe_constexpr(InputPtr data, size_t len,
2195
                                                  OutputPtr utf8_output,
2196
                                                  size_t utf8_len) {
2197
  size_t pos = 0;
2198
  size_t utf8_pos = 0;
2199
  while (pos < len && utf8_pos < utf8_len) {
2200
    const unsigned char byte = data[pos];
2201
    if ((byte & 0x80) == 0) { // if ASCII
2202
      // will generate one UTF-8 bytes
2203
      utf8_output[utf8_pos++] = char(byte);
2204
      pos++;
2205
    } else if (utf8_pos + 2 <= utf8_len) {
2206
      // will generate two UTF-8 bytes
2207
      utf8_output[utf8_pos++] = char((byte >> 6) | 0b11000000);
2208
      utf8_output[utf8_pos++] = char((byte & 0b111111) | 0b10000000);
2209
      pos++;
2210
    } else {
2211
      break;
2212
    }
2213
  }
2214
  return utf8_pos;
2215
}
2216
2217
template <typename InputPtr, typename OutputPtr>
2218
#if SIMDUTF_CPLUSPLUS20
2219
  requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
2220
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2221
#endif
2222
simdutf_constexpr23 full_result convert_safe_with_details_constexpr(
2223
    InputPtr data, size_t len, OutputPtr utf8_output, size_t utf8_len) {
2224
  const size_t output_count =
2225
      convert_safe_constexpr(data, len, utf8_output, utf8_len);
2226
  size_t input_count = 0;
2227
  size_t counted_output = 0;
2228
  while (input_count < len) {
2229
    const size_t width =
2230
        uint8_t(data[input_count]) < uint8_t(0x80) ? size_t(1) : size_t(2);
2231
    if (counted_output + width > output_count) {
2232
      break;
2233
    }
2234
    input_count++;
2235
    counted_output += width;
2236
  }
2237
  return full_result(input_count == len ? error_code::SUCCESS
2238
                                        : error_code::OUTPUT_BUFFER_TOO_SMALL,
2239
                     input_count, output_count);
2240
}
2241
2242
template <typename InputPtr>
2243
#if SIMDUTF_CPLUSPLUS20
2244
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
2245
#endif
2246
simdutf_constexpr23 simdutf_warn_unused size_t
2247
utf8_length_from_latin1(InputPtr input, size_t length) noexcept {
2248
  size_t answer = length;
2249
  size_t i = 0;
2250
2251
#if SIMDUTF_CPLUSPLUS23
2252
  if !consteval
2253
#endif
2254
  {
2255
    auto pop = [](uint64_t v) {
2256
      return (size_t)(((v >> 7) & UINT64_C(0x0101010101010101)) *
2257
                          UINT64_C(0x0101010101010101) >>
2258
                      56);
2259
    };
2260
    for (; i + 32 <= length; i += 32) {
2261
      uint64_t v;
2262
      memcpy(&v, input + i, 8);
2263
      answer += pop(v);
2264
      memcpy(&v, input + i + 8, sizeof(v));
2265
      answer += pop(v);
2266
      memcpy(&v, input + i + 16, sizeof(v));
2267
      answer += pop(v);
2268
      memcpy(&v, input + i + 24, sizeof(v));
2269
      answer += pop(v);
2270
    }
2271
    for (; i + 8 <= length; i += 8) {
2272
      uint64_t v;
2273
      memcpy(&v, input + i, sizeof(v));
2274
      answer += pop(v);
2275
    }
2276
  } // !consteval scope
2277
  for (; i + 1 <= length; i += 1) {
2278
    answer += static_cast<uint8_t>(input[i]) >> 7;
2279
  }
2280
  return answer;
2281
}
2282
2283
} // namespace latin1_to_utf8
2284
} // unnamed namespace
2285
} // namespace scalar
2286
} // namespace simdutf
2287
2288
#endif
2289
/* end file include/simdutf/scalar/latin1_to_utf8/latin1_to_utf8.h */
2290
/* begin file include/simdutf/scalar/utf16.h */
2291
#ifndef SIMDUTF_UTF16_H
2292
#define SIMDUTF_UTF16_H
2293
2294
namespace simdutf {
2295
namespace scalar {
2296
namespace utf16 {
2297
2298
template <endianness big_endian>
2299
simdutf_warn_unused simdutf_constexpr23 bool
2300
validate_as_ascii(const char16_t *data, size_t len) noexcept {
2301
  for (size_t pos = 0; pos < len; pos++) {
2302
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
2303
    if (word >= 0x80) {
2304
      return false;
2305
    }
2306
  }
2307
  return true;
2308
}
2309
2310
template <endianness big_endian>
2311
inline simdutf_warn_unused simdutf_constexpr23 bool
2312
validate(const char16_t *data, size_t len) noexcept {
2313
  uint64_t pos = 0;
2314
  while (pos < len) {
2315
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
2316
    if ((word & 0xF800) == 0xD800) {
2317
      if (pos + 1 >= len) {
2318
        return false;
2319
      }
2320
      char16_t diff = char16_t(word - 0xD800);
2321
      if (diff > 0x3FF) {
2322
        return false;
2323
      }
2324
      char16_t next_word = !match_system(big_endian)
2325
                               ? u16_swap_bytes(data[pos + 1])
2326
                               : data[pos + 1];
2327
      char16_t diff2 = char16_t(next_word - 0xDC00);
2328
      if (diff2 > 0x3FF) {
2329
        return false;
2330
      }
2331
      pos += 2;
2332
    } else {
2333
      pos++;
2334
    }
2335
  }
2336
  return true;
2337
}
2338
2339
template <endianness big_endian>
2340
inline simdutf_warn_unused simdutf_constexpr23 result
2341
validate_with_errors(const char16_t *data, size_t len) noexcept {
2342
  size_t pos = 0;
2343
  while (pos < len) {
2344
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(data[pos]);
2345
    if ((word & 0xF800) == 0xD800) {
2346
      if (pos + 1 >= len) {
2347
        return result(error_code::SURROGATE, pos);
2348
      }
2349
      char16_t diff = char16_t(word - 0xD800);
2350
      if (diff > 0x3FF) {
2351
        return result(error_code::SURROGATE, pos);
2352
      }
2353
      char16_t next_word = !match_system(big_endian)
2354
                               ? u16_swap_bytes(data[pos + 1])
2355
                               : data[pos + 1];
2356
      char16_t diff2 = uint16_t(next_word - 0xDC00);
2357
      if (diff2 > 0x3FF) {
2358
        return result(error_code::SURROGATE, pos);
2359
      }
2360
      pos += 2;
2361
    } else {
2362
      pos++;
2363
    }
2364
  }
2365
  return result(error_code::SUCCESS, pos);
2366
}
2367
2368
template <endianness big_endian>
2369
simdutf_constexpr23 size_t count_code_points(const char16_t *p, size_t len) {
2370
  // We are not BOM aware.
2371
  size_t counter{0};
2372
  for (size_t i = 0; i < len; i++) {
2373
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
2374
    counter += ((word & 0xFC00) != 0xDC00);
2375
  }
2376
  return counter;
2377
}
2378
2379
template <endianness big_endian>
2380
simdutf_constexpr23 size_t utf8_length_from_utf16(const char16_t *p,
2381
                                                  size_t len) {
2382
  // We are not BOM aware.
2383
  size_t counter{0};
2384
  for (size_t i = 0; i < len; i++) {
2385
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
2386
    counter++; // ASCII
2387
    counter += static_cast<size_t>(
2388
        word >
2389
        0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
2390
    counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) ||
2391
                                   (word >= 0xE000)); // three-byte
2392
  }
2393
  return counter;
2394
}
2395
2396
template <endianness big_endian>
2397
simdutf_constexpr23 size_t utf32_length_from_utf16(const char16_t *p,
2398
                                                   size_t len) {
2399
  // We are not BOM aware.
2400
  size_t counter{0};
2401
  for (size_t i = 0; i < len; i++) {
2402
    char16_t word = scalar::utf16::swap_if_needed<big_endian>(p[i]);
2403
    counter += ((word & 0xFC00) != 0xDC00);
2404
  }
2405
  return counter;
2406
}
2407
2408
simdutf_really_inline simdutf_constexpr23 void
2409
0
change_endianness_utf16(const char16_t *input, size_t size, char16_t *output) {
2410
0
  for (size_t i = 0; i < size; i++) {
2411
0
    *output++ = char16_t(input[i] >> 8 | input[i] << 8);
2412
0
  }
2413
0
}
2414
2415
template <endianness big_endian>
2416
simdutf_warn_unused simdutf_constexpr23 size_t
2417
trim_partial_utf16(const char16_t *input, size_t length) {
2418
  if (length == 0) {
2419
    return 0;
2420
  }
2421
  uint16_t last_word = uint16_t(input[length - 1]);
2422
  last_word = scalar::utf16::swap_if_needed<big_endian>(last_word);
2423
  length -= ((last_word & 0xFC00) == 0xD800);
2424
  return length;
2425
}
2426
2427
template <endianness big_endian> constexpr bool is_high_surrogate(char16_t c) {
2428
  c = scalar::utf16::swap_if_needed<big_endian>(c);
2429
  return (0xd800 <= c && c <= 0xdbff);
2430
}
2431
2432
template <endianness big_endian> constexpr bool is_low_surrogate(char16_t c) {
2433
  c = scalar::utf16::swap_if_needed<big_endian>(c);
2434
  return (0xdc00 <= c && c <= 0xdfff);
2435
}
2436
2437
0
simdutf_unused simdutf_really_inline constexpr bool high_surrogate(char16_t c) {
2438
0
  return (0xd800 <= c && c <= 0xdbff);
2439
0
}
2440
2441
template <endianness big_endian>
2442
simdutf_constexpr23 result
2443
utf8_length_from_utf16_with_replacement(const char16_t *p, size_t len) {
2444
  bool any_surrogates = false;
2445
  // We are not BOM aware.
2446
  size_t counter{0};
2447
  for (size_t i = 0; i < len; i++) {
2448
    if (is_high_surrogate<big_endian>(p[i])) {
2449
      any_surrogates = true;
2450
      // surrogate pair
2451
      if (i + 1 < len && is_low_surrogate<big_endian>(p[i + 1])) {
2452
        counter += 4;
2453
        i++; // skip low surrogate
2454
      } else {
2455
        counter += 3; // unpaired high surrogate replaced by U+FFFD
2456
      }
2457
      continue;
2458
    } else if (is_low_surrogate<big_endian>(p[i])) {
2459
      any_surrogates = true;
2460
      counter += 3; // unpaired low surrogate replaced by U+FFFD
2461
      continue;
2462
    }
2463
    char16_t word = !match_system(big_endian) ? u16_swap_bytes(p[i]) : p[i];
2464
    counter++; // at least 1 byte
2465
    counter +=
2466
        static_cast<size_t>(word > 0x7F); // non-ASCII is at least 2 bytes
2467
    counter += static_cast<size_t>(word > 0x7FF); // three-byte
2468
  }
2469
  return {any_surrogates ? error_code::SURROGATE : error_code::SUCCESS,
2470
          counter};
2471
}
2472
2473
// variable templates are a C++14 extension
2474
template <endianness big_endian> constexpr char16_t replacement() {
2475
  return !match_system(big_endian) ? scalar::u16_swap_bytes(0xfffd) : 0xfffd;
2476
}
2477
2478
template <endianness big_endian>
2479
simdutf_constexpr23 void to_well_formed_utf16(const char16_t *input, size_t len,
2480
                                              char16_t *output) {
2481
  const char16_t replacement = utf16::replacement<big_endian>();
2482
  bool high_surrogate_prev = false, high_surrogate, low_surrogate;
2483
  size_t i = 0;
2484
  for (; i < len; i++) {
2485
    char16_t c = input[i];
2486
    high_surrogate = is_high_surrogate<big_endian>(c);
2487
    low_surrogate = is_low_surrogate<big_endian>(c);
2488
    if (high_surrogate_prev && !low_surrogate) {
2489
      output[i - 1] = replacement;
2490
    }
2491
2492
    if (!high_surrogate_prev && low_surrogate) {
2493
      output[i] = replacement;
2494
    } else {
2495
      output[i] = input[i];
2496
    }
2497
    high_surrogate_prev = high_surrogate;
2498
  }
2499
2500
  /* string may not end with high surrogate */
2501
  if (high_surrogate_prev) {
2502
    output[i - 1] = replacement;
2503
  }
2504
}
2505
2506
} // namespace utf16
2507
} // namespace scalar
2508
} // namespace simdutf
2509
2510
#endif
2511
/* end file include/simdutf/scalar/utf16.h */
2512
/* begin file include/simdutf/scalar/utf16_to_latin1/utf16_to_latin1.h */
2513
#ifndef SIMDUTF_UTF16_TO_LATIN1_H
2514
#define SIMDUTF_UTF16_TO_LATIN1_H
2515
2516
#include <cstring> // for std::memcpy
2517
2518
namespace simdutf {
2519
namespace scalar {
2520
namespace {
2521
namespace utf16_to_latin1 {
2522
2523
template <endianness big_endian, typename InputPtr, typename OutputPtr>
2524
#if SIMDUTF_CPLUSPLUS20
2525
  requires(simdutf::detail::indexes_into_utf16<InputPtr> &&
2526
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2527
#endif
2528
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
2529
                                   OutputPtr latin_output) {
2530
  if (len == 0) {
2531
    return 0;
2532
  }
2533
  size_t pos = 0;
2534
  const auto latin_output_start = latin_output;
2535
  uint16_t word = 0;
2536
  uint16_t too_large = 0;
2537
2538
  while (pos < len) {
2539
    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2540
    too_large |= word;
2541
    *latin_output++ = char(word & 0xFF);
2542
    pos++;
2543
  }
2544
  if ((too_large & 0xFF00) != 0) {
2545
    return 0;
2546
  }
2547
2548
  return latin_output - latin_output_start;
2549
}
2550
2551
template <endianness big_endian, typename InputPtr, typename OutputPtr>
2552
#if SIMDUTF_CPLUSPLUS20
2553
  requires(simdutf::detail::indexes_into_utf16<InputPtr> &&
2554
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2555
#endif
2556
simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
2557
                                               OutputPtr latin_output) {
2558
  if (len == 0) {
2559
    return result(error_code::SUCCESS, 0);
2560
  }
2561
  size_t pos = 0;
2562
  auto start = latin_output;
2563
  uint16_t word;
2564
2565
  while (pos < len) {
2566
#if SIMDUTF_CPLUSPLUS23
2567
    if !consteval
2568
#endif
2569
    {
2570
      if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that
2571
                             // they are Latin1
2572
        uint64_t v1, v2, v3, v4;
2573
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
2574
        ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
2575
        ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
2576
        ::memcpy(&v4, data + pos + 12, sizeof(uint64_t));
2577
2578
        if constexpr (!match_system(big_endian)) {
2579
          v1 = (v1 >> 8) | (v1 << (64 - 8));
2580
        }
2581
        if constexpr (!match_system(big_endian)) {
2582
          v2 = (v2 >> 8) | (v2 << (64 - 8));
2583
        }
2584
        if constexpr (!match_system(big_endian)) {
2585
          v3 = (v3 >> 8) | (v3 << (64 - 8));
2586
        }
2587
        if constexpr (!match_system(big_endian)) {
2588
          v4 = (v4 >> 8) | (v4 << (64 - 8));
2589
        }
2590
2591
        if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
2592
          size_t final_pos = pos + 16;
2593
          while (pos < final_pos) {
2594
            *latin_output++ = !match_system(big_endian)
2595
                                  ? char(u16_swap_bytes(data[pos]))
2596
                                  : char(data[pos]);
2597
            pos++;
2598
          }
2599
          continue;
2600
        }
2601
      }
2602
    }
2603
2604
    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2605
    if ((word & 0xFF00) == 0) {
2606
      *latin_output++ = char(word & 0xFF);
2607
      pos++;
2608
    } else {
2609
      return result(error_code::TOO_LARGE, pos);
2610
    }
2611
  }
2612
  return result(error_code::SUCCESS, latin_output - start);
2613
}
2614
2615
} // namespace utf16_to_latin1
2616
} // unnamed namespace
2617
} // namespace scalar
2618
} // namespace simdutf
2619
2620
#endif
2621
/* end file include/simdutf/scalar/utf16_to_latin1/utf16_to_latin1.h */
2622
/* begin file include/simdutf/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */
2623
#ifndef SIMDUTF_VALID_UTF16_TO_LATIN1_H
2624
#define SIMDUTF_VALID_UTF16_TO_LATIN1_H
2625
2626
namespace simdutf {
2627
namespace scalar {
2628
namespace {
2629
namespace utf16_to_latin1 {
2630
2631
template <endianness big_endian, class InputIterator, class OutputIterator>
2632
simdutf_constexpr23 inline size_t
2633
convert_valid_impl(InputIterator data, size_t len,
2634
                   OutputIterator latin_output) {
2635
  static_assert(
2636
      std::is_same<typename std::decay<decltype(*data)>::type, uint16_t>::value,
2637
      "must decay to uint16_t");
2638
  size_t pos = 0;
2639
  const auto start = latin_output;
2640
  uint16_t word = 0;
2641
2642
  while (pos < len) {
2643
    word = !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2644
    *latin_output++ = char(word);
2645
    pos++;
2646
  }
2647
2648
  return latin_output - start;
2649
}
2650
2651
template <endianness big_endian>
2652
simdutf_really_inline size_t convert_valid(const char16_t *buf, size_t len,
2653
                                           char *latin_output) {
2654
  return convert_valid_impl<big_endian>(reinterpret_cast<const uint16_t *>(buf),
2655
                                        len, latin_output);
2656
}
2657
} // namespace utf16_to_latin1
2658
} // unnamed namespace
2659
} // namespace scalar
2660
} // namespace simdutf
2661
2662
#endif
2663
/* end file include/simdutf/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */
2664
/* begin file include/simdutf/scalar/utf16_to_utf32/utf16_to_utf32.h */
2665
#ifndef SIMDUTF_UTF16_TO_UTF32_H
2666
#define SIMDUTF_UTF16_TO_UTF32_H
2667
2668
namespace simdutf {
2669
namespace scalar {
2670
namespace {
2671
namespace utf16_to_utf32 {
2672
2673
template <endianness big_endian>
2674
simdutf_constexpr23 size_t convert(const char16_t *data, size_t len,
2675
                                   char32_t *utf32_output) {
2676
  size_t pos = 0;
2677
  char32_t *start{utf32_output};
2678
  while (pos < len) {
2679
    uint16_t word =
2680
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2681
    if ((word & 0xF800) != 0xD800) {
2682
      // No surrogate pair, extend 16-bit word to 32-bit word
2683
      *utf32_output++ = char32_t(word);
2684
      pos++;
2685
    } else {
2686
      // must be a surrogate pair
2687
      uint16_t diff = uint16_t(word - 0xD800);
2688
      if (diff > 0x3FF) {
2689
        return 0;
2690
      }
2691
      if (pos + 1 >= len) {
2692
        return 0;
2693
      } // minimal bound checking
2694
      uint16_t next_word = !match_system(big_endian)
2695
                               ? u16_swap_bytes(data[pos + 1])
2696
                               : data[pos + 1];
2697
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
2698
      if (diff2 > 0x3FF) {
2699
        return 0;
2700
      }
2701
      uint32_t value = (diff << 10) + diff2 + 0x10000;
2702
      *utf32_output++ = char32_t(value);
2703
      pos += 2;
2704
    }
2705
  }
2706
  return utf32_output - start;
2707
}
2708
2709
template <endianness big_endian>
2710
simdutf_constexpr23 result convert_with_errors(const char16_t *data, size_t len,
2711
                                               char32_t *utf32_output) {
2712
  size_t pos = 0;
2713
  char32_t *start{utf32_output};
2714
  while (pos < len) {
2715
    uint16_t word =
2716
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2717
    if ((word & 0xF800) != 0xD800) {
2718
      // No surrogate pair, extend 16-bit word to 32-bit word
2719
      *utf32_output++ = char32_t(word);
2720
      pos++;
2721
    } else {
2722
      // must be a surrogate pair
2723
      uint16_t diff = uint16_t(word - 0xD800);
2724
      if (diff > 0x3FF) {
2725
        return result(error_code::SURROGATE, pos);
2726
      }
2727
      if (pos + 1 >= len) {
2728
        return result(error_code::SURROGATE, pos);
2729
      } // minimal bound checking
2730
      uint16_t next_word = !match_system(big_endian)
2731
                               ? u16_swap_bytes(data[pos + 1])
2732
                               : data[pos + 1];
2733
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
2734
      if (diff2 > 0x3FF) {
2735
        return result(error_code::SURROGATE, pos);
2736
      }
2737
      uint32_t value = (diff << 10) + diff2 + 0x10000;
2738
      *utf32_output++ = char32_t(value);
2739
      pos += 2;
2740
    }
2741
  }
2742
  return result(error_code::SUCCESS, utf32_output - start);
2743
}
2744
2745
} // namespace utf16_to_utf32
2746
} // unnamed namespace
2747
} // namespace scalar
2748
} // namespace simdutf
2749
2750
#endif
2751
/* end file include/simdutf/scalar/utf16_to_utf32/utf16_to_utf32.h */
2752
/* begin file include/simdutf/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */
2753
#ifndef SIMDUTF_VALID_UTF16_TO_UTF32_H
2754
#define SIMDUTF_VALID_UTF16_TO_UTF32_H
2755
2756
namespace simdutf {
2757
namespace scalar {
2758
namespace {
2759
namespace utf16_to_utf32 {
2760
2761
template <endianness big_endian>
2762
simdutf_constexpr23 size_t convert_valid(const char16_t *data, size_t len,
2763
                                         char32_t *utf32_output) {
2764
  size_t pos = 0;
2765
  char32_t *start{utf32_output};
2766
  while (pos < len) {
2767
    uint16_t word =
2768
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2769
    if ((word & 0xF800) != 0xD800) {
2770
      // No surrogate pair, extend 16-bit word to 32-bit word
2771
      *utf32_output++ = char32_t(word);
2772
      pos++;
2773
    } else {
2774
      // must be a surrogate pair
2775
      uint16_t diff = uint16_t(word - 0xD800);
2776
      if (pos + 1 >= len) {
2777
        return 0;
2778
      } // minimal bound checking
2779
      uint16_t next_word = !match_system(big_endian)
2780
                               ? u16_swap_bytes(data[pos + 1])
2781
                               : data[pos + 1];
2782
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
2783
      uint32_t value = (diff << 10) + diff2 + 0x10000;
2784
      *utf32_output++ = char32_t(value);
2785
      pos += 2;
2786
    }
2787
  }
2788
  return utf32_output - start;
2789
}
2790
2791
} // namespace utf16_to_utf32
2792
} // unnamed namespace
2793
} // namespace scalar
2794
} // namespace simdutf
2795
2796
#endif
2797
/* end file include/simdutf/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */
2798
/* begin file include/simdutf/scalar/utf16_to_utf8/utf16_to_utf8.h */
2799
#ifndef SIMDUTF_UTF16_TO_UTF8_H
2800
#define SIMDUTF_UTF16_TO_UTF8_H
2801
2802
#include <cstring>
2803
2804
namespace simdutf {
2805
namespace scalar {
2806
namespace {
2807
namespace utf16_to_utf8 {
2808
2809
template <endianness big_endian, typename InputPtr, typename OutputPtr>
2810
#if SIMDUTF_CPLUSPLUS20
2811
  requires simdutf::detail::indexes_into_utf16<InputPtr>
2812
// FIXME constrain output as well
2813
#endif
2814
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
2815
                                   OutputPtr utf8_output) {
2816
  size_t pos = 0;
2817
  const auto start = utf8_output;
2818
  while (pos < len) {
2819
#if SIMDUTF_CPLUSPLUS23
2820
    if !consteval
2821
#endif
2822
    {
2823
      // try to convert the next block of 8 bytes
2824
      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
2825
                            // they are ascii
2826
        uint64_t v;
2827
        ::memcpy(&v, data + pos, sizeof(uint64_t));
2828
        if constexpr (!match_system(big_endian)) {
2829
          v = (v >> 8) | (v << (64 - 8));
2830
        }
2831
        if ((v & 0xFF80FF80FF80FF80) == 0) {
2832
          size_t final_pos = pos + 4;
2833
          while (pos < final_pos) {
2834
            *utf8_output++ = !match_system(big_endian)
2835
                                 ? char(u16_swap_bytes(data[pos]))
2836
                                 : char(data[pos]);
2837
            pos++;
2838
          }
2839
          continue;
2840
        }
2841
      }
2842
    }
2843
    uint16_t word =
2844
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2845
    if ((word & 0xFF80) == 0) {
2846
      // will generate one UTF-8 bytes
2847
      *utf8_output++ = char(word);
2848
      pos++;
2849
    } else if ((word & 0xF800) == 0) {
2850
      // will generate two UTF-8 bytes
2851
      // we have 0b110XXXXX 0b10XXXXXX
2852
      *utf8_output++ = char((word >> 6) | 0b11000000);
2853
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
2854
      pos++;
2855
    } else if ((word & 0xF800) != 0xD800) {
2856
      // will generate three UTF-8 bytes
2857
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
2858
      *utf8_output++ = char((word >> 12) | 0b11100000);
2859
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
2860
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
2861
      pos++;
2862
    } else {
2863
      // must be a surrogate pair
2864
      if (pos + 1 >= len) {
2865
        return 0;
2866
      }
2867
      uint16_t diff = uint16_t(word - 0xD800);
2868
      if (diff > 0x3FF) {
2869
        return 0;
2870
      }
2871
      uint16_t next_word = !match_system(big_endian)
2872
                               ? u16_swap_bytes(data[pos + 1])
2873
                               : data[pos + 1];
2874
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
2875
      if (diff2 > 0x3FF) {
2876
        return 0;
2877
      }
2878
      uint32_t value = (diff << 10) + diff2 + 0x10000;
2879
      // will generate four UTF-8 bytes
2880
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
2881
      *utf8_output++ = char((value >> 18) | 0b11110000);
2882
      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
2883
      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
2884
      *utf8_output++ = char((value & 0b111111) | 0b10000000);
2885
      pos += 2;
2886
    }
2887
  }
2888
  return utf8_output - start;
2889
}
2890
2891
template <endianness big_endian, bool check_output = false, typename InputPtr,
2892
          typename OutputPtr>
2893
#if SIMDUTF_CPLUSPLUS20
2894
  requires(simdutf::detail::indexes_into_utf16<InputPtr> &&
2895
           simdutf::detail::index_assignable_from_char<OutputPtr>)
2896
#endif
2897
simdutf_constexpr23 full_result convert_with_errors(InputPtr data, size_t len,
2898
                                                    OutputPtr utf8_output,
2899
                                                    size_t utf8_len = 0) {
2900
  if (check_output && utf8_len == 0) {
2901
    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
2902
  }
2903
2904
  size_t pos = 0;
2905
  auto start = utf8_output;
2906
  auto end = utf8_output + utf8_len;
2907
2908
  while (pos < len) {
2909
#if SIMDUTF_CPLUSPLUS23
2910
    if !consteval
2911
#endif
2912
    {
2913
      // try to convert the next block of 8 bytes
2914
      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
2915
                            // they are ascii
2916
        uint64_t v;
2917
        ::memcpy(&v, data + pos, sizeof(uint64_t));
2918
        if constexpr (!match_system(big_endian))
2919
          v = (v >> 8) | (v << (64 - 8));
2920
        if ((v & 0xFF80FF80FF80FF80) == 0) {
2921
          size_t final_pos = pos + 4;
2922
          while (pos < final_pos) {
2923
            if (check_output && size_t(end - utf8_output) < 1) {
2924
              return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
2925
                                 utf8_output - start);
2926
            }
2927
            *utf8_output++ = !match_system(big_endian)
2928
                                 ? char(u16_swap_bytes(data[pos]))
2929
                                 : char(data[pos]);
2930
            pos++;
2931
          }
2932
          continue;
2933
        }
2934
      }
2935
    }
2936
2937
    uint16_t word =
2938
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
2939
    if ((word & 0xFF80) == 0) {
2940
      // will generate one UTF-8 bytes
2941
      if (check_output && size_t(end - utf8_output) < 1) {
2942
        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
2943
                           utf8_output - start);
2944
      }
2945
      *utf8_output++ = char(word);
2946
      pos++;
2947
    } else if ((word & 0xF800) == 0) {
2948
      // will generate two UTF-8 bytes
2949
      // we have 0b110XXXXX 0b10XXXXXX
2950
      if (check_output && size_t(end - utf8_output) < 2) {
2951
        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
2952
                           utf8_output - start);
2953
      }
2954
      *utf8_output++ = char((word >> 6) | 0b11000000);
2955
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
2956
      pos++;
2957
2958
    } else if ((word & 0xF800) != 0xD800) {
2959
      // will generate three UTF-8 bytes
2960
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
2961
      if (check_output && size_t(end - utf8_output) < 3) {
2962
        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
2963
                           utf8_output - start);
2964
      }
2965
      *utf8_output++ = char((word >> 12) | 0b11100000);
2966
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
2967
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
2968
      pos++;
2969
    } else {
2970
2971
      if (check_output && size_t(end - utf8_output) < 4) {
2972
        return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, pos,
2973
                           utf8_output - start);
2974
      }
2975
      // must be a surrogate pair
2976
      if (pos + 1 >= len) {
2977
        return full_result(error_code::SURROGATE, pos, utf8_output - start);
2978
      }
2979
      uint16_t diff = uint16_t(word - 0xD800);
2980
      if (diff > 0x3FF) {
2981
        return full_result(error_code::SURROGATE, pos, utf8_output - start);
2982
      }
2983
      uint16_t next_word = !match_system(big_endian)
2984
                               ? u16_swap_bytes(data[pos + 1])
2985
                               : data[pos + 1];
2986
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
2987
      if (diff2 > 0x3FF) {
2988
        return full_result(error_code::SURROGATE, pos, utf8_output - start);
2989
      }
2990
      uint32_t value = (diff << 10) + diff2 + 0x10000;
2991
      // will generate four UTF-8 bytes
2992
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
2993
      *utf8_output++ = char((value >> 18) | 0b11110000);
2994
      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
2995
      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
2996
      *utf8_output++ = char((value & 0b111111) | 0b10000000);
2997
      pos += 2;
2998
    }
2999
  }
3000
  return full_result(error_code::SUCCESS, pos, utf8_output - start);
3001
}
3002
3003
template <endianness big_endian>
3004
inline result simple_convert_with_errors(const char16_t *buf, size_t len,
3005
                                         char *utf8_output) {
3006
  return convert_with_errors<big_endian, false>(buf, len, utf8_output, 0);
3007
}
3008
3009
template <endianness big_endian>
3010
simdutf_constexpr23 size_t convert_with_replacement(const char16_t *data,
3011
                                                    size_t len,
3012
                                                    char *utf8_output) {
3013
  size_t pos = 0;
3014
  char *start = utf8_output;
3015
  while (pos < len) {
3016
#if SIMDUTF_CPLUSPLUS23
3017
    if !consteval
3018
#endif
3019
    {
3020
      // try to convert the next block of 8 bytes
3021
      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
3022
                            // they are ascii
3023
        uint64_t v;
3024
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3025
        if constexpr (!match_system(big_endian)) {
3026
          v = (v >> 8) | (v << (64 - 8));
3027
        }
3028
        if ((v & 0xFF80FF80FF80FF80) == 0) {
3029
          size_t final_pos = pos + 4;
3030
          while (pos < final_pos) {
3031
            *utf8_output++ = !match_system(big_endian)
3032
                                 ? char(u16_swap_bytes(data[pos]))
3033
                                 : char(data[pos]);
3034
            pos++;
3035
          }
3036
          continue;
3037
        }
3038
      }
3039
    }
3040
    uint16_t word =
3041
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
3042
    if ((word & 0xFF80) == 0) {
3043
      // will generate one UTF-8 bytes
3044
      *utf8_output++ = char(word);
3045
      pos++;
3046
    } else if ((word & 0xF800) == 0) {
3047
      // will generate two UTF-8 bytes
3048
      // we have 0b110XXXXX 0b10XXXXXX
3049
      *utf8_output++ = char((word >> 6) | 0b11000000);
3050
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3051
      pos++;
3052
    } else if ((word & 0xF800) != 0xD800) {
3053
      // will generate three UTF-8 bytes
3054
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
3055
      *utf8_output++ = char((word >> 12) | 0b11100000);
3056
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3057
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3058
      pos++;
3059
    } else {
3060
      // surrogate range
3061
      uint16_t diff = uint16_t(word - 0xD800);
3062
      if (diff <= 0x3FF && pos + 1 < len) {
3063
        // high surrogate, check for valid pair
3064
        uint16_t next_word = !match_system(big_endian)
3065
                                 ? u16_swap_bytes(data[pos + 1])
3066
                                 : data[pos + 1];
3067
        uint16_t diff2 = uint16_t(next_word - 0xDC00);
3068
        if (diff2 <= 0x3FF) {
3069
          // valid surrogate pair
3070
          uint32_t value = (diff << 10) + diff2 + 0x10000;
3071
          // will generate four UTF-8 bytes
3072
          *utf8_output++ = char((value >> 18) | 0b11110000);
3073
          *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
3074
          *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
3075
          *utf8_output++ = char((value & 0b111111) | 0b10000000);
3076
          pos += 2;
3077
          continue;
3078
        }
3079
      }
3080
      // unpaired surrogate: replace with U+FFFD (0xEF 0xBF 0xBD)
3081
      *utf8_output++ = char(0xef);
3082
      *utf8_output++ = char(0xbf);
3083
      *utf8_output++ = char(0xbd);
3084
      pos++;
3085
    }
3086
  }
3087
  return utf8_output - start;
3088
}
3089
3090
template <endianness big_endian, typename InputPtr, typename OutputPtr>
3091
#if SIMDUTF_CPLUSPLUS20
3092
  requires(simdutf::detail::indexes_into_utf16<InputPtr> &&
3093
           simdutf::detail::index_assignable_from_char<OutputPtr>)
3094
#endif
3095
simdutf_constexpr23 full_result convert_with_replacement_safe(
3096
    InputPtr data, size_t len, OutputPtr utf8_output, size_t utf8_len) {
3097
  if (len == 0) {
3098
    return full_result(error_code::SUCCESS, 0, 0);
3099
  }
3100
  if (utf8_len == 0) {
3101
    return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, 0, 0);
3102
  }
3103
3104
  size_t input_count = 0;
3105
  size_t output_count = 0;
3106
  while (input_count < len) {
3107
    full_result r = convert_with_errors<big_endian, true>(
3108
        data + input_count, len - input_count, utf8_output + output_count,
3109
        utf8_len - output_count);
3110
    input_count += r.input_count;
3111
    output_count += r.output_count;
3112
3113
    if (r.error == error_code::SUCCESS) {
3114
      return full_result(error_code::SUCCESS, input_count, output_count);
3115
    }
3116
3117
    if (r.error == error_code::OUTPUT_BUFFER_TOO_SMALL) {
3118
      if (utf8_len - output_count < 3) {
3119
        return full_result(r.error, input_count, output_count);
3120
      }
3121
3122
      uint16_t word = !match_system(big_endian)
3123
                          ? u16_swap_bytes(data[input_count])
3124
                          : data[input_count];
3125
      bool unpaired = (word & 0xfc00) == 0xdc00;
3126
      if ((word & 0xfc00) == 0xd800) {
3127
        if (input_count + 1 == len) {
3128
          unpaired = true;
3129
        } else {
3130
          const uint16_t next_word = !match_system(big_endian)
3131
                                         ? u16_swap_bytes(data[input_count + 1])
3132
                                         : data[input_count + 1];
3133
          unpaired = (next_word & 0xfc00) != 0xdc00;
3134
        }
3135
      }
3136
      if (!unpaired) {
3137
        return full_result(r.error, input_count, output_count);
3138
      }
3139
    } else if (r.error != error_code::SURROGATE) {
3140
      return full_result(r.error, input_count, output_count);
3141
    }
3142
3143
    if (utf8_len - output_count < 3) {
3144
      return full_result(error_code::OUTPUT_BUFFER_TOO_SMALL, input_count,
3145
                         output_count);
3146
    }
3147
    utf8_output[output_count++] = char(0xef);
3148
    utf8_output[output_count++] = char(0xbf);
3149
    utf8_output[output_count++] = char(0xbd);
3150
    input_count++;
3151
  }
3152
  return full_result(error_code::SUCCESS, input_count, output_count);
3153
}
3154
3155
} // namespace utf16_to_utf8
3156
} // unnamed namespace
3157
} // namespace scalar
3158
} // namespace simdutf
3159
3160
#endif
3161
/* end file include/simdutf/scalar/utf16_to_utf8/utf16_to_utf8.h */
3162
/* begin file include/simdutf/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */
3163
#ifndef SIMDUTF_VALID_UTF16_TO_UTF8_H
3164
#define SIMDUTF_VALID_UTF16_TO_UTF8_H
3165
3166
#include <cstring>
3167
3168
namespace simdutf {
3169
namespace scalar {
3170
namespace {
3171
namespace utf16_to_utf8 {
3172
3173
template <endianness big_endian, typename InputPtr, typename OutputPtr>
3174
#if SIMDUTF_CPLUSPLUS20
3175
  requires(simdutf::detail::indexes_into_utf16<InputPtr> &&
3176
           simdutf::detail::index_assignable_from_char<OutputPtr>)
3177
#endif
3178
simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len,
3179
                                         OutputPtr utf8_output) {
3180
  size_t pos = 0;
3181
  auto start = utf8_output;
3182
  while (pos < len) {
3183
#if SIMDUTF_CPLUSPLUS23
3184
    if !consteval
3185
#endif
3186
    {
3187
      // try to convert the next block of 4 ASCII characters
3188
      if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that
3189
                            // they are ascii
3190
        uint64_t v;
3191
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3192
        if constexpr (!match_system(big_endian)) {
3193
          v = (v >> 8) | (v << (64 - 8));
3194
        }
3195
        if ((v & 0xFF80FF80FF80FF80) == 0) {
3196
          size_t final_pos = pos + 4;
3197
          while (pos < final_pos) {
3198
            *utf8_output++ = !match_system(big_endian)
3199
                                 ? char(u16_swap_bytes(data[pos]))
3200
                                 : char(data[pos]);
3201
            pos++;
3202
          }
3203
          continue;
3204
        }
3205
      }
3206
    }
3207
3208
    uint16_t word =
3209
        !match_system(big_endian) ? u16_swap_bytes(data[pos]) : data[pos];
3210
    if ((word & 0xFF80) == 0) {
3211
      // will generate one UTF-8 bytes
3212
      *utf8_output++ = char(word);
3213
      pos++;
3214
    } else if ((word & 0xF800) == 0) {
3215
      // will generate two UTF-8 bytes
3216
      // we have 0b110XXXXX 0b10XXXXXX
3217
      *utf8_output++ = char((word >> 6) | 0b11000000);
3218
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3219
      pos++;
3220
    } else if ((word & 0xF800) != 0xD800) {
3221
      // will generate three UTF-8 bytes
3222
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
3223
      *utf8_output++ = char((word >> 12) | 0b11100000);
3224
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3225
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3226
      pos++;
3227
    } else {
3228
      // must be a surrogate pair
3229
      uint16_t diff = uint16_t(word - 0xD800);
3230
      if (pos + 1 >= len) {
3231
        return 0;
3232
      } // minimal bound checking
3233
      uint16_t next_word = !match_system(big_endian)
3234
                               ? u16_swap_bytes(data[pos + 1])
3235
                               : data[pos + 1];
3236
      uint16_t diff2 = uint16_t(next_word - 0xDC00);
3237
      uint32_t value = (diff << 10) + diff2 + 0x10000;
3238
      // will generate four UTF-8 bytes
3239
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
3240
      *utf8_output++ = char((value >> 18) | 0b11110000);
3241
      *utf8_output++ = char(((value >> 12) & 0b111111) | 0b10000000);
3242
      *utf8_output++ = char(((value >> 6) & 0b111111) | 0b10000000);
3243
      *utf8_output++ = char((value & 0b111111) | 0b10000000);
3244
      pos += 2;
3245
    }
3246
  }
3247
  return utf8_output - start;
3248
}
3249
3250
} // namespace utf16_to_utf8
3251
} // unnamed namespace
3252
} // namespace scalar
3253
} // namespace simdutf
3254
3255
#endif
3256
/* end file include/simdutf/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */
3257
/* begin file include/simdutf/scalar/utf32.h */
3258
#ifndef SIMDUTF_UTF32_H
3259
#define SIMDUTF_UTF32_H
3260
3261
namespace simdutf {
3262
namespace scalar {
3263
namespace utf32 {
3264
3265
template <typename InputPtr>
3266
#if SIMDUTF_CPLUSPLUS20
3267
  requires simdutf::detail::indexes_into_uint32<InputPtr>
3268
#endif
3269
simdutf_warn_unused simdutf_constexpr23 bool validate(InputPtr data,
3270
0
                                                      size_t len) noexcept {
3271
0
  uint64_t pos = 0;
3272
0
  for (; pos < len; pos++) {
3273
0
    uint32_t word = data[pos];
3274
0
    if (word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) {
3275
0
      return false;
3276
0
    }
3277
0
  }
3278
0
  return true;
3279
0
}
3280
3281
simdutf_warn_unused simdutf_really_inline bool validate(const char32_t *buf,
3282
0
                                                        size_t len) noexcept {
3283
0
  return validate(reinterpret_cast<const uint32_t *>(buf), len);
3284
0
}
3285
3286
template <typename InputPtr>
3287
#if SIMDUTF_CPLUSPLUS20
3288
  requires simdutf::detail::indexes_into_uint32<InputPtr>
3289
#endif
3290
simdutf_warn_unused simdutf_constexpr23 result
3291
0
validate_with_errors(InputPtr data, size_t len) noexcept {
3292
0
  size_t pos = 0;
3293
0
  for (; pos < len; pos++) {
3294
0
    uint32_t word = data[pos];
3295
0
    if (word > 0x10FFFF) {
3296
0
      return result(error_code::TOO_LARGE, pos);
3297
0
    }
3298
0
    if (word >= 0xD800 && word <= 0xDFFF) {
3299
0
      return result(error_code::SURROGATE, pos);
3300
0
    }
3301
0
  }
3302
0
  return result(error_code::SUCCESS, pos);
3303
0
}
3304
3305
simdutf_warn_unused simdutf_really_inline result
3306
0
validate_with_errors(const char32_t *buf, size_t len) noexcept {
3307
0
  return validate_with_errors(reinterpret_cast<const uint32_t *>(buf), len);
3308
0
}
3309
3310
inline simdutf_constexpr23 size_t utf8_length_from_utf32(const char32_t *p,
3311
0
                                                         size_t len) {
3312
0
  // We are not BOM aware.
3313
0
  size_t counter{0};
3314
0
  for (size_t i = 0; i < len; i++) {
3315
0
    // credit: @ttsugriy  for the vectorizable approach
3316
0
    counter++;                                     // ASCII
3317
0
    counter += static_cast<size_t>(p[i] > 0x7F);   // two-byte
3318
0
    counter += static_cast<size_t>(p[i] > 0x7FF);  // three-byte
3319
0
    counter += static_cast<size_t>(p[i] > 0xFFFF); // four-bytes
3320
0
  }
3321
0
  return counter;
3322
0
}
3323
3324
inline simdutf_warn_unused simdutf_constexpr23 size_t
3325
0
utf16_length_from_utf32(const char32_t *p, size_t len) {
3326
0
  // We are not BOM aware.
3327
0
  size_t counter{0};
3328
0
  for (size_t i = 0; i < len; i++) {
3329
0
    counter++;                                     // non-surrogate word
3330
0
    counter += static_cast<size_t>(p[i] > 0xFFFF); // surrogate pair
3331
0
  }
3332
0
  return counter;
3333
0
}
3334
3335
} // namespace utf32
3336
} // namespace scalar
3337
} // namespace simdutf
3338
3339
#endif
3340
/* end file include/simdutf/scalar/utf32.h */
3341
/* begin file include/simdutf/scalar/utf32_to_latin1/utf32_to_latin1.h */
3342
#ifndef SIMDUTF_UTF32_TO_LATIN1_H
3343
#define SIMDUTF_UTF32_TO_LATIN1_H
3344
3345
#include <cstring>
3346
3347
namespace simdutf {
3348
namespace scalar {
3349
namespace {
3350
namespace utf32_to_latin1 {
3351
3352
inline simdutf_constexpr23 size_t convert(const char32_t *data, size_t len,
3353
0
                                          char *latin1_output) {
3354
0
  char *start = latin1_output;
3355
0
  uint32_t utf32_char;
3356
0
  size_t pos = 0;
3357
0
  uint32_t too_large = 0;
3358
0
3359
0
  while (pos < len) {
3360
0
    utf32_char = (uint32_t)data[pos];
3361
0
    too_large |= utf32_char;
3362
0
    *latin1_output++ = (char)(utf32_char & 0xFF);
3363
0
    pos++;
3364
0
  }
3365
0
  if ((too_large & 0xFFFFFF00) != 0) {
3366
0
    return 0;
3367
0
  }
3368
0
  return latin1_output - start;
3369
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert(char32_t const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert(char32_t const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert(char32_t const*, unsigned long, char*)
3370
3371
inline simdutf_constexpr23 result convert_with_errors(const char32_t *data,
3372
                                                      size_t len,
3373
0
                                                      char *latin1_output) {
3374
0
  char *start{latin1_output};
3375
0
  size_t pos = 0;
3376
0
  while (pos < len) {
3377
0
#if SIMDUTF_CPLUSPLUS23
3378
0
    if !consteval
3379
0
#endif
3380
0
    {
3381
0
      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
3382
0
                            // they are Latin1
3383
0
        uint64_t v;
3384
0
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3385
0
        if ((v & 0xFFFFFF00FFFFFF00) == 0) {
3386
0
          *latin1_output++ = char(data[pos]);
3387
0
          *latin1_output++ = char(data[pos + 1]);
3388
0
          pos += 2;
3389
0
          continue;
3390
0
        }
3391
0
      }
3392
0
    }
3393
0
3394
0
    uint32_t utf32_char = data[pos];
3395
0
    if ((utf32_char & 0xFFFFFF00) ==
3396
0
        0) { // Check if the character can be represented in Latin-1
3397
0
      *latin1_output++ = (char)(utf32_char & 0xFF);
3398
0
      pos++;
3399
0
    } else {
3400
0
      return result(error_code::TOO_LARGE, pos);
3401
0
    };
3402
0
  }
3403
0
  return result(error_code::SUCCESS, latin1_output - start);
3404
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_with_errors(char32_t const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_with_errors(char32_t const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_with_errors(char32_t const*, unsigned long, char*)
3405
3406
} // namespace utf32_to_latin1
3407
} // unnamed namespace
3408
} // namespace scalar
3409
} // namespace simdutf
3410
3411
#endif
3412
/* end file include/simdutf/scalar/utf32_to_latin1/utf32_to_latin1.h */
3413
/* begin file include/simdutf/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */
3414
#ifndef SIMDUTF_VALID_UTF32_TO_LATIN1_H
3415
#define SIMDUTF_VALID_UTF32_TO_LATIN1_H
3416
3417
#include <cstring>
3418
3419
namespace simdutf {
3420
namespace scalar {
3421
namespace {
3422
namespace utf32_to_latin1 {
3423
3424
template <typename ReadPtr, typename WritePtr>
3425
simdutf_constexpr23 size_t convert_valid(ReadPtr data, size_t len,
3426
0
                                         WritePtr latin1_output) {
3427
0
  static_assert(
3428
0
      std::is_same<typename std::decay<decltype(*data)>::type, uint32_t>::value,
3429
0
      "dereferencing the data pointer must result in a uint32_t");
3430
0
  auto start = latin1_output;
3431
0
  uint32_t utf32_char;
3432
0
  size_t pos = 0;
3433
0
3434
0
  while (pos < len) {
3435
0
    utf32_char = data[pos];
3436
0
3437
0
#if SIMDUTF_CPLUSPLUS23
3438
0
    // avoid using the 8 byte at a time optimization in constant evaluation
3439
0
    // mode. memcpy can't be used and replacing it with bitwise or gave worse
3440
0
    // codegen (when not during constant evaluation).
3441
0
    if !consteval {
3442
0
#endif
3443
0
      if (pos + 2 <= len) {
3444
0
        // if it is safe to read 8 more bytes, check that they are Latin1
3445
0
        uint64_t v;
3446
0
        std::memcpy(&v, data + pos, sizeof(uint64_t));
3447
0
        if ((v & 0xFFFFFF00FFFFFF00) == 0) {
3448
0
          *latin1_output++ = char(data[pos]);
3449
0
          *latin1_output++ = char(data[pos + 1]);
3450
0
          pos += 2;
3451
0
          continue;
3452
0
        } else {
3453
0
          // output can not be represented in latin1
3454
0
          return 0;
3455
0
        }
3456
0
      }
3457
0
#if SIMDUTF_CPLUSPLUS23
3458
0
    } // if ! consteval
3459
0
#endif
3460
0
    if ((utf32_char & 0xFFFFFF00) == 0) {
3461
0
      *latin1_output++ = char(utf32_char);
3462
0
    } else {
3463
0
      // output can not be represented in latin1
3464
0
      return 0;
3465
0
    }
3466
0
    pos++;
3467
0
  }
3468
0
  return latin1_output - start;
3469
0
}
Unexecuted instantiation: parser.cpp:unsigned long simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid<unsigned int const*, char*>(unsigned int const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:unsigned long simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid<unsigned int const*, char*>(unsigned int const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid<unsigned int const*, char*>(unsigned int const*, unsigned long, char*)
3470
3471
simdutf_really_inline size_t convert_valid(const char32_t *buf, size_t len,
3472
0
                                           char *latin1_output) {
3473
0
  return convert_valid(reinterpret_cast<const uint32_t *>(buf), len,
3474
0
                       latin1_output);
3475
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid(char32_t const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid(char32_t const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf32_to_latin1::convert_valid(char32_t const*, unsigned long, char*)
3476
3477
} // namespace utf32_to_latin1
3478
} // unnamed namespace
3479
} // namespace scalar
3480
} // namespace simdutf
3481
3482
#endif
3483
/* end file include/simdutf/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */
3484
/* begin file include/simdutf/scalar/utf32_to_utf16/utf32_to_utf16.h */
3485
#ifndef SIMDUTF_UTF32_TO_UTF16_H
3486
#define SIMDUTF_UTF32_TO_UTF16_H
3487
3488
namespace simdutf {
3489
namespace scalar {
3490
namespace {
3491
namespace utf32_to_utf16 {
3492
3493
template <endianness big_endian>
3494
simdutf_constexpr23 size_t convert(const char32_t *data, size_t len,
3495
                                   char16_t *utf16_output) {
3496
  size_t pos = 0;
3497
  char16_t *start{utf16_output};
3498
  while (pos < len) {
3499
    uint32_t word = data[pos];
3500
    if ((word & 0xFFFF0000) == 0) {
3501
      if (word >= 0xD800 && word <= 0xDFFF) {
3502
        return 0;
3503
      }
3504
      // will not generate a surrogate pair
3505
      *utf16_output++ = !match_system(big_endian)
3506
                            ? char16_t(u16_swap_bytes(uint16_t(word)))
3507
                            : char16_t(word);
3508
    } else {
3509
      // will generate a surrogate pair
3510
      if (word > 0x10FFFF) {
3511
        return 0;
3512
      }
3513
      word -= 0x10000;
3514
      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
3515
      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
3516
      if constexpr (!match_system(big_endian)) {
3517
        high_surrogate = u16_swap_bytes(high_surrogate);
3518
        low_surrogate = u16_swap_bytes(low_surrogate);
3519
      }
3520
      *utf16_output++ = char16_t(high_surrogate);
3521
      *utf16_output++ = char16_t(low_surrogate);
3522
    }
3523
    pos++;
3524
  }
3525
  return utf16_output - start;
3526
}
3527
3528
template <endianness big_endian>
3529
simdutf_constexpr23 result convert_with_errors(const char32_t *data, size_t len,
3530
                                               char16_t *utf16_output) {
3531
  size_t pos = 0;
3532
  char16_t *start{utf16_output};
3533
  while (pos < len) {
3534
    uint32_t word = data[pos];
3535
    if ((word & 0xFFFF0000) == 0) {
3536
      if (word >= 0xD800 && word <= 0xDFFF) {
3537
        return result(error_code::SURROGATE, pos);
3538
      }
3539
      // will not generate a surrogate pair
3540
      *utf16_output++ = !match_system(big_endian)
3541
                            ? char16_t(u16_swap_bytes(uint16_t(word)))
3542
                            : char16_t(word);
3543
    } else {
3544
      // will generate a surrogate pair
3545
      if (word > 0x10FFFF) {
3546
        return result(error_code::TOO_LARGE, pos);
3547
      }
3548
      word -= 0x10000;
3549
      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
3550
      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
3551
      if constexpr (!match_system(big_endian)) {
3552
        high_surrogate = u16_swap_bytes(high_surrogate);
3553
        low_surrogate = u16_swap_bytes(low_surrogate);
3554
      }
3555
      *utf16_output++ = char16_t(high_surrogate);
3556
      *utf16_output++ = char16_t(low_surrogate);
3557
    }
3558
    pos++;
3559
  }
3560
  return result(error_code::SUCCESS, utf16_output - start);
3561
}
3562
3563
} // namespace utf32_to_utf16
3564
} // unnamed namespace
3565
} // namespace scalar
3566
} // namespace simdutf
3567
3568
#endif
3569
/* end file include/simdutf/scalar/utf32_to_utf16/utf32_to_utf16.h */
3570
/* begin file include/simdutf/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */
3571
#ifndef SIMDUTF_VALID_UTF32_TO_UTF16_H
3572
#define SIMDUTF_VALID_UTF32_TO_UTF16_H
3573
3574
namespace simdutf {
3575
namespace scalar {
3576
namespace {
3577
namespace utf32_to_utf16 {
3578
3579
template <endianness big_endian>
3580
simdutf_constexpr23 size_t convert_valid(const char32_t *data, size_t len,
3581
                                         char16_t *utf16_output) {
3582
  size_t pos = 0;
3583
  char16_t *start{utf16_output};
3584
  while (pos < len) {
3585
    uint32_t word = data[pos];
3586
    if ((word & 0xFFFF0000) == 0) {
3587
      // will not generate a surrogate pair
3588
      *utf16_output++ = !match_system(big_endian)
3589
                            ? char16_t(u16_swap_bytes(uint16_t(word)))
3590
                            : char16_t(word);
3591
      pos++;
3592
    } else {
3593
      // will generate a surrogate pair
3594
      word -= 0x10000;
3595
      uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
3596
      uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
3597
      if constexpr (!match_system(big_endian)) {
3598
        high_surrogate = u16_swap_bytes(high_surrogate);
3599
        low_surrogate = u16_swap_bytes(low_surrogate);
3600
      }
3601
      *utf16_output++ = char16_t(high_surrogate);
3602
      *utf16_output++ = char16_t(low_surrogate);
3603
      pos++;
3604
    }
3605
  }
3606
  return utf16_output - start;
3607
}
3608
3609
} // namespace utf32_to_utf16
3610
} // unnamed namespace
3611
} // namespace scalar
3612
} // namespace simdutf
3613
3614
#endif
3615
/* end file include/simdutf/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */
3616
/* begin file include/simdutf/scalar/utf32_to_utf8/utf32_to_utf8.h */
3617
#ifndef SIMDUTF_UTF32_TO_UTF8_H
3618
#define SIMDUTF_UTF32_TO_UTF8_H
3619
3620
#include <cstring>
3621
3622
namespace simdutf {
3623
namespace scalar {
3624
namespace {
3625
namespace utf32_to_utf8 {
3626
3627
template <typename InputPtr, typename OutputPtr>
3628
#if SIMDUTF_CPLUSPLUS20
3629
  requires(simdutf::detail::indexes_into_utf32<InputPtr> &&
3630
           simdutf::detail::index_assignable_from_char<OutputPtr>)
3631
#endif
3632
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
3633
                                   OutputPtr utf8_output) {
3634
  size_t pos = 0;
3635
  auto start = utf8_output;
3636
  while (pos < len) {
3637
#if SIMDUTF_CPLUSPLUS23
3638
    if !consteval
3639
#endif
3640
    { // try to convert the next block of 2 ASCII characters
3641
      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
3642
                            // they are ascii
3643
        uint64_t v;
3644
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3645
        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
3646
          *utf8_output++ = char(data[pos]);
3647
          *utf8_output++ = char(data[pos + 1]);
3648
          pos += 2;
3649
          continue;
3650
        }
3651
      }
3652
    }
3653
3654
    uint32_t word = data[pos];
3655
    if ((word & 0xFFFFFF80) == 0) {
3656
      // will generate one UTF-8 bytes
3657
      *utf8_output++ = char(word);
3658
      pos++;
3659
    } else if ((word & 0xFFFFF800) == 0) {
3660
      // will generate two UTF-8 bytes
3661
      // we have 0b110XXXXX 0b10XXXXXX
3662
      *utf8_output++ = char((word >> 6) | 0b11000000);
3663
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3664
      pos++;
3665
    } else if ((word & 0xFFFF0000) == 0) {
3666
      // will generate three UTF-8 bytes
3667
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
3668
      if (word >= 0xD800 && word <= 0xDFFF) {
3669
        return 0;
3670
      }
3671
      *utf8_output++ = char((word >> 12) | 0b11100000);
3672
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3673
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3674
      pos++;
3675
    } else {
3676
      // will generate four UTF-8 bytes
3677
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
3678
      if (word > 0x10FFFF) {
3679
        return 0;
3680
      }
3681
      *utf8_output++ = char((word >> 18) | 0b11110000);
3682
      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
3683
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3684
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3685
      pos++;
3686
    }
3687
  }
3688
  return utf8_output - start;
3689
}
3690
3691
template <typename InputPtr, typename OutputPtr>
3692
#if SIMDUTF_CPLUSPLUS20
3693
  requires(simdutf::detail::indexes_into_utf32<InputPtr> &&
3694
           simdutf::detail::index_assignable_from_char<OutputPtr>)
3695
#endif
3696
simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
3697
                                               OutputPtr utf8_output) {
3698
  size_t pos = 0;
3699
  auto start = utf8_output;
3700
  while (pos < len) {
3701
#if SIMDUTF_CPLUSPLUS23
3702
    if !consteval
3703
#endif
3704
    { // try to convert the next block of 2 ASCII characters
3705
      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
3706
                            // they are ascii
3707
        uint64_t v;
3708
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3709
        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
3710
          *utf8_output++ = char(data[pos]);
3711
          *utf8_output++ = char(data[pos + 1]);
3712
          pos += 2;
3713
          continue;
3714
        }
3715
      }
3716
    }
3717
3718
    uint32_t word = data[pos];
3719
    if ((word & 0xFFFFFF80) == 0) {
3720
      // will generate one UTF-8 bytes
3721
      *utf8_output++ = char(word);
3722
      pos++;
3723
    } else if ((word & 0xFFFFF800) == 0) {
3724
      // will generate two UTF-8 bytes
3725
      // we have 0b110XXXXX 0b10XXXXXX
3726
      *utf8_output++ = char((word >> 6) | 0b11000000);
3727
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3728
      pos++;
3729
    } else if ((word & 0xFFFF0000) == 0) {
3730
      // will generate three UTF-8 bytes
3731
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
3732
      if (word >= 0xD800 && word <= 0xDFFF) {
3733
        return result(error_code::SURROGATE, pos);
3734
      }
3735
      *utf8_output++ = char((word >> 12) | 0b11100000);
3736
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3737
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3738
      pos++;
3739
    } else {
3740
      // will generate four UTF-8 bytes
3741
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
3742
      if (word > 0x10FFFF) {
3743
        return result(error_code::TOO_LARGE, pos);
3744
      }
3745
      *utf8_output++ = char((word >> 18) | 0b11110000);
3746
      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
3747
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3748
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3749
      pos++;
3750
    }
3751
  }
3752
  return result(error_code::SUCCESS, utf8_output - start);
3753
}
3754
3755
} // namespace utf32_to_utf8
3756
} // unnamed namespace
3757
} // namespace scalar
3758
} // namespace simdutf
3759
3760
#endif
3761
/* end file include/simdutf/scalar/utf32_to_utf8/utf32_to_utf8.h */
3762
/* begin file include/simdutf/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */
3763
#ifndef SIMDUTF_VALID_UTF32_TO_UTF8_H
3764
#define SIMDUTF_VALID_UTF32_TO_UTF8_H
3765
3766
#include <cstring>
3767
3768
namespace simdutf {
3769
namespace scalar {
3770
namespace {
3771
namespace utf32_to_utf8 {
3772
3773
template <typename InputPtr, typename OutputPtr>
3774
#if SIMDUTF_CPLUSPLUS20
3775
  requires(simdutf::detail::indexes_into_utf32<InputPtr> &&
3776
           simdutf::detail::index_assignable_from_char<OutputPtr>)
3777
#endif
3778
simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len,
3779
                                         OutputPtr utf8_output) {
3780
  size_t pos = 0;
3781
  auto start = utf8_output;
3782
  while (pos < len) {
3783
#if SIMDUTF_CPLUSPLUS23
3784
    if !consteval
3785
#endif
3786
    { // try to convert the next block of 2 ASCII characters
3787
      if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that
3788
                            // they are ascii
3789
        uint64_t v;
3790
        ::memcpy(&v, data + pos, sizeof(uint64_t));
3791
        if ((v & 0xFFFFFF80FFFFFF80) == 0) {
3792
          *utf8_output++ = char(data[pos]);
3793
          *utf8_output++ = char(data[pos + 1]);
3794
          pos += 2;
3795
          continue;
3796
        }
3797
      }
3798
    }
3799
3800
    uint32_t word = data[pos];
3801
    if ((word & 0xFFFFFF80) == 0) {
3802
      // will generate one UTF-8 bytes
3803
      *utf8_output++ = char(word);
3804
      pos++;
3805
    } else if ((word & 0xFFFFF800) == 0) {
3806
      // will generate two UTF-8 bytes
3807
      // we have 0b110XXXXX 0b10XXXXXX
3808
      *utf8_output++ = char((word >> 6) | 0b11000000);
3809
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3810
      pos++;
3811
    } else if ((word & 0xFFFF0000) == 0) {
3812
      // will generate three UTF-8 bytes
3813
      // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
3814
      *utf8_output++ = char((word >> 12) | 0b11100000);
3815
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3816
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3817
      pos++;
3818
    } else {
3819
      // will generate four UTF-8 bytes
3820
      // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
3821
      *utf8_output++ = char((word >> 18) | 0b11110000);
3822
      *utf8_output++ = char(((word >> 12) & 0b111111) | 0b10000000);
3823
      *utf8_output++ = char(((word >> 6) & 0b111111) | 0b10000000);
3824
      *utf8_output++ = char((word & 0b111111) | 0b10000000);
3825
      pos++;
3826
    }
3827
  }
3828
  return utf8_output - start;
3829
}
3830
3831
} // namespace utf32_to_utf8
3832
} // unnamed namespace
3833
} // namespace scalar
3834
} // namespace simdutf
3835
3836
#endif
3837
/* end file include/simdutf/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */
3838
/* begin file include/simdutf/scalar/utf8.h */
3839
#ifndef SIMDUTF_UTF8_H
3840
#define SIMDUTF_UTF8_H
3841
3842
#include <cstring>
3843
3844
namespace simdutf {
3845
namespace scalar {
3846
namespace {
3847
namespace utf8 {
3848
3849
// credit: based on code from Google Fuchsia (Apache Licensed)
3850
template <class BytePtr>
3851
simdutf_constexpr23 simdutf_warn_unused bool validate(BytePtr data,
3852
0
                                                      size_t len) noexcept {
3853
0
  static_assert(
3854
0
      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
3855
0
      "dereferencing the data pointer must result in a uint8_t");
3856
0
  uint64_t pos = 0;
3857
0
  uint32_t code_point = 0;
3858
0
  while (pos < len) {
3859
0
    uint64_t next_pos;
3860
0
#if SIMDUTF_CPLUSPLUS23
3861
0
    if !consteval
3862
0
#endif
3863
0
    { // check if the next 16 bytes are ascii.
3864
0
      next_pos = pos + 16;
3865
0
      if (next_pos <= len) { // if it is safe to read 16 more bytes, check
3866
0
                             // that they are ascii
3867
0
        uint64_t v1{};
3868
0
        std::memcpy(&v1, data + pos, sizeof(uint64_t));
3869
0
        uint64_t v2{};
3870
0
        std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
3871
0
        uint64_t v{v1 | v2};
3872
0
        if ((v & 0x8080808080808080) == 0) {
3873
0
          pos = next_pos;
3874
0
          continue;
3875
0
        }
3876
0
      }
3877
0
    }
3878
0
3879
0
    unsigned char byte = data[pos];
3880
0
3881
0
    while (byte < 0b10000000) {
3882
0
      if (++pos == len) {
3883
0
        return true;
3884
0
      }
3885
0
      byte = data[pos];
3886
0
    }
3887
0
3888
0
    if ((byte & 0b11100000) == 0b11000000) {
3889
0
      next_pos = pos + 2;
3890
0
      if (next_pos > len) {
3891
0
        return false;
3892
0
      }
3893
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
3894
0
        return false;
3895
0
      }
3896
0
      // range check
3897
0
      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
3898
0
      if (code_point < 0x80) {
3899
0
        return false;
3900
0
      }
3901
0
    } else if ((byte & 0b11110000) == 0b11100000) {
3902
0
      next_pos = pos + 3;
3903
0
      if (next_pos > len) {
3904
0
        return false;
3905
0
      }
3906
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
3907
0
        return false;
3908
0
      }
3909
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
3910
0
        return false;
3911
0
      }
3912
0
      // range check
3913
0
      code_point = (byte & 0b00001111) << 12 |
3914
0
                   (data[pos + 1] & 0b00111111) << 6 |
3915
0
                   (data[pos + 2] & 0b00111111);
3916
0
      if ((code_point < 0x800) ||
3917
0
          (0xd7ff < code_point && code_point < 0xe000)) {
3918
0
        return false;
3919
0
      }
3920
0
    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
3921
0
      next_pos = pos + 4;
3922
0
      if (next_pos > len) {
3923
0
        return false;
3924
0
      }
3925
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
3926
0
        return false;
3927
0
      }
3928
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
3929
0
        return false;
3930
0
      }
3931
0
      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
3932
0
        return false;
3933
0
      }
3934
0
      // range check
3935
0
      code_point =
3936
0
          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
3937
0
          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
3938
0
      if (code_point <= 0xffff || 0x10ffff < code_point) {
3939
0
        return false;
3940
0
      }
3941
0
    } else {
3942
0
      // we may have a continuation
3943
0
      return false;
3944
0
    }
3945
0
    pos = next_pos;
3946
0
  }
3947
0
  return true;
3948
0
}
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::utf8::validate<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::utf8::validate<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::utf8::validate<unsigned char const*>(unsigned char const*, unsigned long)
3949
3950
simdutf_really_inline simdutf_warn_unused bool validate(const char *buf,
3951
0
                                                        size_t len) noexcept {
3952
0
  return validate(reinterpret_cast<const uint8_t *>(buf), len);
3953
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate(char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate(char const*, unsigned long)
3954
3955
template <class BytePtr>
3956
simdutf_constexpr23 simdutf_warn_unused result
3957
0
validate_with_errors(BytePtr data, size_t len) noexcept {
3958
0
  static_assert(
3959
0
      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
3960
0
      "dereferencing the data pointer must result in a uint8_t");
3961
0
  size_t pos = 0;
3962
0
  uint32_t code_point = 0;
3963
0
  while (pos < len) {
3964
0
    // check of the next 16 bytes are ascii.
3965
0
    size_t next_pos = pos + 16;
3966
0
    if (next_pos <=
3967
0
        len) { // if it is safe to read 16 more bytes, check that they are ascii
3968
0
      uint64_t v1;
3969
0
      std::memcpy(&v1, data + pos, sizeof(uint64_t));
3970
0
      uint64_t v2;
3971
0
      std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
3972
0
      uint64_t v{v1 | v2};
3973
0
      if ((v & 0x8080808080808080) == 0) {
3974
0
        pos = next_pos;
3975
0
        continue;
3976
0
      }
3977
0
    }
3978
0
    unsigned char byte = data[pos];
3979
0
3980
0
    while (byte < 0b10000000) {
3981
0
      if (++pos == len) {
3982
0
        return result(error_code::SUCCESS, len);
3983
0
      }
3984
0
      byte = data[pos];
3985
0
    }
3986
0
3987
0
    if ((byte & 0b11100000) == 0b11000000) {
3988
0
      next_pos = pos + 2;
3989
0
      if (next_pos > len) {
3990
0
        return result(error_code::TOO_SHORT, pos);
3991
0
      }
3992
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
3993
0
        return result(error_code::TOO_SHORT, pos);
3994
0
      }
3995
0
      // range check
3996
0
      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
3997
0
      if (code_point < 0x80) {
3998
0
        return result(error_code::OVERLONG, pos);
3999
0
      }
4000
0
    } else if ((byte & 0b11110000) == 0b11100000) {
4001
0
      next_pos = pos + 3;
4002
0
      if (next_pos > len) {
4003
0
        return result(error_code::TOO_SHORT, pos);
4004
0
      }
4005
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4006
0
        return result(error_code::TOO_SHORT, pos);
4007
0
      }
4008
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4009
0
        return result(error_code::TOO_SHORT, pos);
4010
0
      }
4011
0
      // range check
4012
0
      code_point = (byte & 0b00001111) << 12 |
4013
0
                   (data[pos + 1] & 0b00111111) << 6 |
4014
0
                   (data[pos + 2] & 0b00111111);
4015
0
      if (code_point < 0x800) {
4016
0
        return result(error_code::OVERLONG, pos);
4017
0
      }
4018
0
      if (0xd7ff < code_point && code_point < 0xe000) {
4019
0
        return result(error_code::SURROGATE, pos);
4020
0
      }
4021
0
    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
4022
0
      next_pos = pos + 4;
4023
0
      if (next_pos > len) {
4024
0
        return result(error_code::TOO_SHORT, pos);
4025
0
      }
4026
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4027
0
        return result(error_code::TOO_SHORT, pos);
4028
0
      }
4029
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4030
0
        return result(error_code::TOO_SHORT, pos);
4031
0
      }
4032
0
      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
4033
0
        return result(error_code::TOO_SHORT, pos);
4034
0
      }
4035
0
      // range check
4036
0
      code_point =
4037
0
          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
4038
0
          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
4039
0
      if (code_point <= 0xffff) {
4040
0
        return result(error_code::OVERLONG, pos);
4041
0
      }
4042
0
      if (0x10ffff < code_point) {
4043
0
        return result(error_code::TOO_LARGE, pos);
4044
0
      }
4045
0
    } else {
4046
0
      // we either have too many continuation bytes or an invalid leading byte
4047
0
      if ((byte & 0b11000000) == 0b10000000) {
4048
0
        return result(error_code::TOO_LONG, pos);
4049
0
      } else {
4050
0
        return result(error_code::HEADER_BITS, pos);
4051
0
      }
4052
0
    }
4053
0
    pos = next_pos;
4054
0
  }
4055
0
  return result(error_code::SUCCESS, len);
4056
0
}
Unexecuted instantiation: parser.cpp:simdutf::result simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::result simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::result simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors<unsigned char const*>(unsigned char const*, unsigned long)
4057
4058
simdutf_really_inline simdutf_warn_unused result
4059
0
validate_with_errors(const char *buf, size_t len) noexcept {
4060
0
  return validate_with_errors(reinterpret_cast<const uint8_t *>(buf), len);
4061
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors(char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_errors(char const*, unsigned long)
4062
4063
// Finds the previous leading byte starting backward from buf and validates with
4064
// errors from there Used to pinpoint the location of an error when an invalid
4065
// chunk is detected We assume that the stream starts with a leading byte, and
4066
// to check that it is the case, we ask that you pass a pointer to the start of
4067
// the stream (start). Note that the resulting count is underflowed if an error
4068
// is encountered in the rewinded segment.
4069
inline simdutf_warn_unused result rewind_and_validate_with_errors(
4070
0
    const char *start, const char *buf, size_t len) noexcept {
4071
0
  // First check that we start with a leading byte
4072
0
  if ((*start & 0b11000000) == 0b10000000) {
4073
0
    return result(error_code::TOO_LONG, 0);
4074
0
  }
4075
0
  size_t extra_len{0};
4076
0
  // A leading byte cannot be further than 4 bytes away
4077
0
  for (int i = 0; i < 5; i++) {
4078
0
    unsigned char byte = *buf;
4079
0
    if ((byte & 0b11000000) != 0b10000000) {
4080
0
      break;
4081
0
    } else {
4082
0
      buf--;
4083
0
      extra_len++;
4084
0
    }
4085
0
  }
4086
0
4087
0
  result res = validate_with_errors(buf, len + extra_len);
4088
0
  res.count -= extra_len; // Might underflow
4089
0
  return res;
4090
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_errors(char const*, char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_errors(char const*, char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_errors(char const*, char const*, unsigned long)
4091
4092
// credit: based on code from Google Fuchsia (Apache Licensed)
4093
template <class BytePtr>
4094
simdutf_constexpr23 simdutf_warn_unused utf8_result
4095
0
validate_with_counts(BytePtr data, size_t len) noexcept {
4096
0
  static_assert(
4097
0
      std::is_same<typename std::decay<decltype(*data)>::type, uint8_t>::value,
4098
0
      "dereferencing the data pointer must result in a uint8_t");
4099
0
  size_t pos = 0;
4100
0
  uint32_t code_point = 0;
4101
0
  size_t continuations = 0;
4102
0
  size_t four_byte = 0;
4103
0
  while (pos < len) {
4104
0
    // std::memcpy is not usable during constant evaluation, so the block-wise
4105
0
    // ASCII fast path is restricted to run time, like in validate() above.
4106
0
#if SIMDUTF_CPLUSPLUS23
4107
0
    if !consteval
4108
0
#endif
4109
0
    { // check if the next 16 bytes are ascii.
4110
0
      const size_t next_ascii_pos = pos + 16;
4111
0
      if (next_ascii_pos <= len) { // if it is safe to read 16 more bytes, check
4112
0
                                   // that they are ascii
4113
0
        uint64_t v1{};
4114
0
        std::memcpy(&v1, data + pos, sizeof(uint64_t));
4115
0
        uint64_t v2{};
4116
0
        std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4117
0
        uint64_t v{v1 | v2};
4118
0
        if ((v & 0x8080808080808080) == 0) {
4119
0
          pos = next_ascii_pos;
4120
0
          continue;
4121
0
        }
4122
0
      }
4123
0
    }
4124
0
4125
0
    size_t next_pos;
4126
0
    unsigned char byte = data[pos];
4127
0
4128
0
    while (byte < 0b10000000) {
4129
0
      if (++pos == len) {
4130
0
        return utf8_result(error_code::SUCCESS, pos, continuations, four_byte);
4131
0
      }
4132
0
      byte = data[pos];
4133
0
    }
4134
0
4135
0
    if ((byte & 0b11100000) == 0b11000000) {
4136
0
      next_pos = pos + 2;
4137
0
      if (next_pos > len) {
4138
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4139
0
                           four_byte);
4140
0
      }
4141
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4142
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4143
0
                           four_byte);
4144
0
      }
4145
0
      // range check
4146
0
      code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
4147
0
      if (code_point < 0x80) {
4148
0
        return utf8_result(error_code::OVERLONG, pos, continuations, four_byte);
4149
0
      }
4150
0
      continuations += 1;
4151
0
    } else if ((byte & 0b11110000) == 0b11100000) {
4152
0
      next_pos = pos + 3;
4153
0
      if (next_pos > len) {
4154
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4155
0
                           four_byte);
4156
0
      }
4157
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4158
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4159
0
                           four_byte);
4160
0
      }
4161
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4162
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4163
0
                           four_byte);
4164
0
      }
4165
0
      // range check
4166
0
      code_point = (byte & 0b00001111) << 12 |
4167
0
                   (data[pos + 1] & 0b00111111) << 6 |
4168
0
                   (data[pos + 2] & 0b00111111);
4169
0
      if (code_point < 0x800) {
4170
0
        return utf8_result(error_code::OVERLONG, pos, continuations, four_byte);
4171
0
      } else if (0xd7ff < code_point && code_point < 0xe000) {
4172
0
        return utf8_result(error_code::SURROGATE, pos, continuations,
4173
0
                           four_byte);
4174
0
      }
4175
0
      continuations += 2;
4176
0
    } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
4177
0
      next_pos = pos + 4;
4178
0
      if (next_pos > len) {
4179
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4180
0
                           four_byte);
4181
0
      }
4182
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4183
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4184
0
                           four_byte);
4185
0
      }
4186
0
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4187
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4188
0
                           four_byte);
4189
0
      }
4190
0
      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
4191
0
        return utf8_result(error_code::TOO_SHORT, pos, continuations,
4192
0
                           four_byte);
4193
0
      }
4194
0
      // range check
4195
0
      code_point =
4196
0
          (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
4197
0
          (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
4198
0
      if (code_point <= 0xffff) {
4199
0
        return utf8_result(error_code::OVERLONG, pos, continuations, four_byte);
4200
0
      } else if (0x10ffff < code_point) {
4201
0
        return utf8_result(error_code::TOO_LARGE, pos, continuations,
4202
0
                           four_byte);
4203
0
      }
4204
0
      continuations += 3;
4205
0
      four_byte += 1;
4206
0
    } else {
4207
0
      // Continuation byte or invalid header byte
4208
0
      if ((byte & 0b11000000) == 0b10000000) {
4209
0
        return utf8_result(error_code::TOO_LONG, pos, continuations, four_byte);
4210
0
      } else {
4211
0
        return utf8_result(error_code::HEADER_BITS, pos, continuations,
4212
0
                           four_byte);
4213
0
      }
4214
0
    }
4215
0
    pos = next_pos;
4216
0
  }
4217
0
  return utf8_result(error_code::SUCCESS, pos, continuations, four_byte);
4218
0
}
Unexecuted instantiation: parser.cpp:simdutf::utf8_result simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::utf8_result simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts<unsigned char const*>(unsigned char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::utf8_result simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts<unsigned char const*>(unsigned char const*, unsigned long)
4219
4220
simdutf_really_inline simdutf_warn_unused utf8_result
4221
0
validate_with_counts(const char *buf, size_t len) noexcept {
4222
0
  return validate_with_counts(reinterpret_cast<const uint8_t *>(buf), len);
4223
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts(char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8::validate_with_counts(char const*, unsigned long)
4224
4225
// Finds the previous leading byte starting backward from buf and validates from
4226
// there on out while counting continuation bytes and 4-byte leads. Used to
4227
// pinpoint the location of an error when an invalid chunk is detected. We check
4228
// that the stream starts with a leading byte with the passed pointer to the
4229
// start of the stream (start).
4230
//
4231
// All three fields of the result are relative to buf. The bytes we rewind over
4232
// sit in a block the caller has already counted, so their contribution is
4233
// subtracted back out at the end. When the error lies inside the rewound
4234
// region, those three subtractions underflow. That is intended, not an
4235
// oversight: the caller adds the offset of buf and the counts of the preceding
4236
// blocks, which include the rewound bytes, and the unsigned wraparound cancels
4237
// exactly, leaving the correct absolute values.
4238
inline simdutf_warn_unused utf8_result rewind_and_validate_with_counts(
4239
0
    const char *start, const char *buf, size_t len) noexcept {
4240
0
  // First check that we start with a leading byte
4241
0
  if ((*start & 0b11000000) == 0b10000000) {
4242
0
    return utf8_result(error_code::TOO_LONG, 0, 0, 0);
4243
0
  }
4244
0
  // If this is the start of the buffer, we don't need to rewind to check the
4245
0
  // previous chunk.
4246
0
  if (start == buf) {
4247
0
    return validate_with_counts(buf, len);
4248
0
  }
4249
0
  // As this isn't the start of the buffer, the error might be in the previous
4250
0
  // chunk. So we backtrack and also need to avoid double counting.
4251
0
  size_t extra_len{0};
4252
0
  size_t extra_continuations{0};
4253
0
  size_t extra_four_bytes{0};
4254
0
  // Four steps are enough. Everything before buf has already validated, so at
4255
0
  // most three continuation bytes can precede it, from a four-byte sequence
4256
0
  // ending exactly on the boundary; the fourth step then lands on its leading
4257
0
  // byte. Note that the sibling rewind_and_validate_with_errors needs five
4258
0
  // steps only because it tests the byte before stepping back, whereas we step
4259
0
  // back first. Callers pass a block boundary at least 64 bytes into the
4260
0
  // stream, so stepping back four bytes stays in bounds.
4261
0
  for (int i = 0; i < 4; i++) {
4262
0
    // We rewind at least one byte to find the actual previous leading byte.
4263
0
    // We do so to backtrack into the last chunk in case the utf8 error happened
4264
0
    // there already.
4265
0
    buf--;
4266
0
    unsigned char byte = *buf;
4267
0
    extra_len++;
4268
0
4269
0
    if (byte >= 0b10000000) {
4270
0
      if (byte >= 0b11110000) {
4271
0
        extra_four_bytes += 1;
4272
0
      } else if ((int8_t)byte < -65 + 1) {
4273
0
        extra_continuations += 1;
4274
0
      }
4275
0
    }
4276
0
    if ((byte & 0b11000000) != 0b10000000) {
4277
0
      break;
4278
0
    }
4279
0
  }
4280
0
4281
0
  utf8_result res = validate_with_counts(buf, len + extra_len);
4282
0
  res.input_count -= extra_len;
4283
0
  res.continuation_count -= extra_continuations;
4284
0
  res.four_byte_count -= extra_four_bytes;
4285
0
  return res;
4286
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_counts(char const*, char const*, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_counts(char const*, char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8::rewind_and_validate_with_counts(char const*, char const*, unsigned long)
4287
4288
template <typename InputPtr>
4289
#if SIMDUTF_CPLUSPLUS20
4290
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4291
#endif
4292
simdutf_constexpr23 size_t count_code_points(InputPtr data, size_t len) {
4293
  size_t counter{0};
4294
  for (size_t i = 0; i < len; i++) {
4295
    // -65 is 0b10111111, anything larger in two-complement's should start a new
4296
    // code point.
4297
    if (int8_t(data[i]) > -65) {
4298
      counter++;
4299
    }
4300
  }
4301
  return counter;
4302
}
4303
4304
template <typename InputPtr>
4305
#if SIMDUTF_CPLUSPLUS20
4306
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4307
#endif
4308
simdutf_constexpr23 size_t utf16_length_from_utf8(InputPtr data, size_t len) {
4309
  size_t counter{0};
4310
  for (size_t i = 0; i < len; i++) {
4311
    if (int8_t(data[i]) > -65) {
4312
      counter++;
4313
    }
4314
    if (uint8_t(data[i]) >= 240) {
4315
      counter++;
4316
    }
4317
  }
4318
  return counter;
4319
}
4320
4321
template <typename InputPtr>
4322
#if SIMDUTF_CPLUSPLUS20
4323
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4324
#endif
4325
simdutf_warn_unused simdutf_constexpr23 size_t
4326
trim_partial_utf8(InputPtr input, size_t length) {
4327
  if (length < 3) {
4328
    switch (length) {
4329
    case 2:
4330
      if (uint8_t(input[length - 1]) >= 0xc0) {
4331
        return length - 1;
4332
      } // 2-, 3- and 4-byte characters with only 1 byte left
4333
      if (uint8_t(input[length - 2]) >= 0xe0) {
4334
        return length - 2;
4335
      } // 3- and 4-byte characters with only 2 bytes left
4336
      return length;
4337
    case 1:
4338
      if (uint8_t(input[length - 1]) >= 0xc0) {
4339
        return length - 1;
4340
      } // 2-, 3- and 4-byte characters with only 1 byte left
4341
      return length;
4342
    case 0:
4343
      return length;
4344
    }
4345
  }
4346
  if (uint8_t(input[length - 1]) >= 0xc0) {
4347
    return length - 1;
4348
  } // 2-, 3- and 4-byte characters with only 1 byte left
4349
  if (uint8_t(input[length - 2]) >= 0xe0) {
4350
    return length - 2;
4351
  } // 3- and 4-byte characters with only 1 byte left
4352
  if (uint8_t(input[length - 3]) >= 0xf0) {
4353
    return length - 3;
4354
  } // 4-byte characters with only 3 bytes left
4355
  return length;
4356
}
4357
4358
} // namespace utf8
4359
} // unnamed namespace
4360
} // namespace scalar
4361
} // namespace simdutf
4362
4363
#endif
4364
/* end file include/simdutf/scalar/utf8.h */
4365
/* begin file include/simdutf/scalar/utf8_to_latin1/utf8_to_latin1.h */
4366
#ifndef SIMDUTF_UTF8_TO_LATIN1_H
4367
#define SIMDUTF_UTF8_TO_LATIN1_H
4368
4369
#include <cstring>
4370
4371
namespace simdutf {
4372
namespace scalar {
4373
namespace {
4374
namespace utf8_to_latin1 {
4375
4376
template <typename InputPtr, typename OutputPtr>
4377
#if SIMDUTF_CPLUSPLUS20
4378
  requires(simdutf::detail::indexes_into_byte_like<InputPtr> &&
4379
           simdutf::detail::indexes_into_byte_like<OutputPtr>)
4380
#endif
4381
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
4382
                                   OutputPtr latin_output) {
4383
  size_t pos = 0;
4384
  auto start = latin_output;
4385
4386
  while (pos < len) {
4387
#if SIMDUTF_CPLUSPLUS23
4388
    if !consteval
4389
#endif
4390
    {
4391
      // try to convert the next block of 16 ASCII bytes
4392
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
4393
                             // they are ascii
4394
        uint64_t v1;
4395
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
4396
        uint64_t v2;
4397
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4398
        uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
4399
                             // 1000 1000 .... etc
4400
        if ((v & 0x8080808080808080) ==
4401
            0) { // if NONE of these are set, e.g. all of them are zero, then
4402
                 // everything is ASCII
4403
          size_t final_pos = pos + 16;
4404
          while (pos < final_pos) {
4405
            *latin_output++ = char(data[pos]);
4406
            pos++;
4407
          }
4408
          continue;
4409
        }
4410
      }
4411
    }
4412
4413
    // suppose it is not an all ASCII byte sequence
4414
    uint8_t leading_byte = data[pos]; // leading byte
4415
    if (leading_byte < 0b10000000) {
4416
      // converting one ASCII byte !!!
4417
      *latin_output++ = char(leading_byte);
4418
      pos++;
4419
    } else if ((leading_byte & 0b11100000) ==
4420
               0b11000000) { // the first three bits indicate:
4421
      // We have a two-byte UTF-8
4422
      if (pos + 1 >= len) {
4423
        return 0;
4424
      } // minimal bound checking
4425
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4426
        return 0;
4427
      } // checks if the next byte is a valid continuation byte in UTF-8. A
4428
        // valid continuation byte starts with 10.
4429
      // range check -
4430
      uint32_t code_point =
4431
          (leading_byte & 0b00011111) << 6 |
4432
          (data[pos + 1] &
4433
           0b00111111); // assembles the Unicode code point from the two bytes.
4434
                        // It does this by discarding the leading 110 and 10
4435
                        // bits from the two bytes, shifting the remaining bits
4436
                        // of the first byte, and then combining the results
4437
                        // with a bitwise OR operation.
4438
      if (code_point < 0x80 || 0xFF < code_point) {
4439
        return 0; // We only care about the range 129-255 which is Non-ASCII
4440
                  // latin1 characters. A code_point beneath 0x80 is invalid as
4441
                  // it is already covered by bytes whose leading bit is zero.
4442
      }
4443
      *latin_output++ = char(code_point);
4444
      pos += 2;
4445
    } else {
4446
      return 0;
4447
    }
4448
  }
4449
  return latin_output - start;
4450
}
4451
4452
template <typename InputPtr>
4453
#if SIMDUTF_CPLUSPLUS20
4454
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4455
#endif
4456
simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
4457
0
                                               char *latin_output) {
4458
0
  size_t pos = 0;
4459
0
  char *start{latin_output};
4460
0
4461
0
  while (pos < len) {
4462
0
#if SIMDUTF_CPLUSPLUS23
4463
0
    if !consteval
4464
0
#endif
4465
0
    {
4466
0
      // try to convert the next block of 16 ASCII bytes
4467
0
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
4468
0
                             // they are ascii
4469
0
        uint64_t v1;
4470
0
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
4471
0
        uint64_t v2;
4472
0
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4473
0
        uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000
4474
0
                             // 1000 1000...etc
4475
0
        if ((v & 0x8080808080808080) ==
4476
0
            0) { // if NONE of these are set, e.g. all of them are zero, then
4477
0
                 // everything is ASCII
4478
0
          size_t final_pos = pos + 16;
4479
0
          while (pos < final_pos) {
4480
0
            *latin_output++ = char(data[pos]);
4481
0
            pos++;
4482
0
          }
4483
0
          continue;
4484
0
        }
4485
0
      }
4486
0
    }
4487
0
    // suppose it is not an all ASCII byte sequence
4488
0
    uint8_t leading_byte = data[pos]; // leading byte
4489
0
    if (leading_byte < 0b10000000) {
4490
0
      // converting one ASCII byte !!!
4491
0
      *latin_output++ = char(leading_byte);
4492
0
      pos++;
4493
0
    } else if ((leading_byte & 0b11100000) ==
4494
0
               0b11000000) { // the first three bits indicate:
4495
0
      // We have a two-byte UTF-8
4496
0
      if (pos + 1 >= len) {
4497
0
        return result(error_code::TOO_SHORT, pos);
4498
0
      } // minimal bound checking
4499
0
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4500
0
        return result(error_code::TOO_SHORT, pos);
4501
0
      } // checks if the next byte is a valid continuation byte in UTF-8. A
4502
0
        // valid continuation byte starts with 10.
4503
0
      // range check -
4504
0
      uint32_t code_point =
4505
0
          (leading_byte & 0b00011111) << 6 |
4506
0
          (data[pos + 1] &
4507
0
           0b00111111); // assembles the Unicode code point from the two bytes.
4508
0
                        // It does this by discarding the leading 110 and 10
4509
0
                        // bits from the two bytes, shifting the remaining bits
4510
0
                        // of the first byte, and then combining the results
4511
0
                        // with a bitwise OR operation.
4512
0
      if (code_point < 0x80) {
4513
0
        return result(error_code::OVERLONG, pos);
4514
0
      }
4515
0
      if (0xFF < code_point) {
4516
0
        return result(error_code::TOO_LARGE, pos);
4517
0
      } // We only care about the range 129-255 which is Non-ASCII latin1
4518
0
        // characters
4519
0
      *latin_output++ = char(code_point);
4520
0
      pos += 2;
4521
0
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
4522
0
      // We have a three-byte UTF-8
4523
0
      return result(error_code::TOO_LARGE, pos);
4524
0
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
4525
0
      // we have a 4-byte UTF-8 word.
4526
0
      return result(error_code::TOO_LARGE, pos);
4527
0
    } else {
4528
0
      // we either have too many continuation bytes or an invalid leading byte
4529
0
      if ((leading_byte & 0b11000000) == 0b10000000) {
4530
0
        return result(error_code::TOO_LONG, pos);
4531
0
      }
4532
0
4533
0
      return result(error_code::HEADER_BITS, pos);
4534
0
    }
4535
0
  }
4536
0
  return result(error_code::SUCCESS, latin_output - start);
4537
0
}
Unexecuted instantiation: parser.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin119convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPc
Unexecuted instantiation: base64.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin119convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPc
Unexecuted instantiation: simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_114utf8_to_latin119convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPc
4538
4539
inline result rewind_and_convert_with_errors(size_t prior_bytes,
4540
                                             const char *buf, size_t len,
4541
0
                                             char *latin1_output) {
4542
0
  size_t extra_len{0};
4543
0
  // We potentially need to go back in time and find a leading byte.
4544
0
  // In theory '3' would be sufficient, but sometimes the error can go back
4545
0
  // quite far.
4546
0
  size_t how_far_back = prior_bytes;
4547
0
  // size_t how_far_back = 3; // 3 bytes in the past + current position
4548
0
  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
4549
0
  bool found_leading_bytes{false};
4550
0
  // important: it is i <= how_far_back and not 'i < how_far_back'.
4551
0
  for (size_t i = 0; i <= how_far_back; i++) {
4552
0
    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
4553
0
    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
4554
0
    if (found_leading_bytes) {
4555
0
      if (i > 0 && byte < 128) {
4556
0
        // If we had to go back and the leading byte is ascii
4557
0
        // then we can stop right away.
4558
0
        return result(error_code::TOO_LONG, 0 - i + 1);
4559
0
      }
4560
0
      buf -= i;
4561
0
      extra_len = i;
4562
0
      break;
4563
0
    }
4564
0
  }
4565
0
  //
4566
0
  // It is possible for this function to return a negative count in its result.
4567
0
  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
4568
0
  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
4569
0
  // unsigned integral type of the result of the sizeof operator
4570
0
  //
4571
0
  // An unsigned type will simply wrap round arithmetically (well defined).
4572
0
  //
4573
0
  if (!found_leading_bytes) {
4574
0
    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
4575
0
    // [....] [continuation] [continuation] [continuation] | [buf is
4576
0
    // continuation] Or we possibly have a stream that does not start with a
4577
0
    // leading byte.
4578
0
    return result(error_code::TOO_LONG, 0 - how_far_back);
4579
0
  }
4580
0
  result res = convert_with_errors(buf, len + extra_len, latin1_output);
4581
0
  if (res.error) {
4582
0
    res.count -= extra_len;
4583
0
  }
4584
0
  return res;
4585
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_latin1::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_latin1::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_latin1::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char*)
4586
4587
} // namespace utf8_to_latin1
4588
} // unnamed namespace
4589
} // namespace scalar
4590
} // namespace simdutf
4591
4592
#endif
4593
/* end file include/simdutf/scalar/utf8_to_latin1/utf8_to_latin1.h */
4594
/* begin file include/simdutf/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */
4595
#ifndef SIMDUTF_VALID_UTF8_TO_LATIN1_H
4596
#define SIMDUTF_VALID_UTF8_TO_LATIN1_H
4597
4598
#include <cstring>
4599
4600
namespace simdutf {
4601
namespace scalar {
4602
namespace {
4603
namespace utf8_to_latin1 {
4604
4605
template <typename InputPtr>
4606
#if SIMDUTF_CPLUSPLUS20
4607
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4608
#endif
4609
simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len,
4610
                                         char *latin_output) {
4611
4612
  size_t pos = 0;
4613
  char *start{latin_output};
4614
4615
  while (pos < len) {
4616
#if SIMDUTF_CPLUSPLUS23
4617
    if !consteval
4618
#endif
4619
    {
4620
      // try to convert the next block of 16 ASCII bytes
4621
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
4622
                             // they are ascii
4623
        uint64_t v1;
4624
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
4625
        uint64_t v2;
4626
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4627
        uint64_t v{v1 |
4628
                   v2}; // We are only interested in these bits: 1000 1000 1000
4629
                        // 1000, so it makes sense to concatenate everything
4630
        if ((v & 0x8080808080808080) ==
4631
            0) { // if NONE of these are set, e.g. all of them are zero, then
4632
                 // everything is ASCII
4633
          size_t final_pos = pos + 16;
4634
          while (pos < final_pos) {
4635
            *latin_output++ = uint8_t(data[pos]);
4636
            pos++;
4637
          }
4638
          continue;
4639
        }
4640
      }
4641
    }
4642
4643
    // suppose it is not an all ASCII byte sequence
4644
    auto leading_byte = uint8_t(data[pos]); // leading byte
4645
    if (leading_byte < 0b10000000) {
4646
      // converting one ASCII byte !!!
4647
      *latin_output++ = char(leading_byte);
4648
      pos++;
4649
    } else if ((leading_byte & 0b11100000) ==
4650
               0b11000000) { // the first three bits indicate:
4651
      // We have a two-byte UTF-8
4652
      if (pos + 1 >= len) {
4653
        break;
4654
      } // minimal bound checking
4655
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
4656
        return 0;
4657
      } // checks if the next byte is a valid continuation byte in UTF-8. A
4658
        // valid continuation byte starts with 10.
4659
      // range check -
4660
      uint32_t code_point =
4661
          (leading_byte & 0b00011111) << 6 |
4662
          (uint8_t(data[pos + 1]) &
4663
           0b00111111); // assembles the Unicode code point from the two bytes.
4664
                        // It does this by discarding the leading 110 and 10
4665
                        // bits from the two bytes, shifting the remaining bits
4666
                        // of the first byte, and then combining the results
4667
                        // with a bitwise OR operation.
4668
      *latin_output++ = char(code_point);
4669
      pos += 2;
4670
    } else {
4671
      // we may have a continuation but we do not do error checking
4672
      return 0;
4673
    }
4674
  }
4675
  return latin_output - start;
4676
}
4677
4678
} // namespace utf8_to_latin1
4679
} // unnamed namespace
4680
} // namespace scalar
4681
} // namespace simdutf
4682
4683
#endif
4684
/* end file include/simdutf/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */
4685
/* begin file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16.h */
4686
#ifndef SIMDUTF_UTF8_TO_UTF16_H
4687
#define SIMDUTF_UTF8_TO_UTF16_H
4688
4689
#include <cstring>
4690
4691
namespace simdutf {
4692
namespace scalar {
4693
namespace {
4694
namespace utf8_to_utf16 {
4695
4696
template <endianness big_endian, typename InputPtr>
4697
#if SIMDUTF_CPLUSPLUS20
4698
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4699
#endif
4700
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
4701
                                   char16_t *utf16_output) {
4702
  size_t pos = 0;
4703
  char16_t *start{utf16_output};
4704
  while (pos < len) {
4705
#if SIMDUTF_CPLUSPLUS23
4706
    if !consteval
4707
#endif
4708
    // try to convert the next block of 16 ASCII bytes
4709
    {
4710
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
4711
                             // they are ascii
4712
        uint64_t v1;
4713
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
4714
        uint64_t v2;
4715
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4716
        uint64_t v{v1 | v2};
4717
        if ((v & 0x8080808080808080) == 0) {
4718
          size_t final_pos = pos + 16;
4719
          while (pos < final_pos) {
4720
            *utf16_output++ = !match_system(big_endian)
4721
                                  ? char16_t(u16_swap_bytes(data[pos]))
4722
                                  : char16_t(data[pos]);
4723
            pos++;
4724
          }
4725
          continue;
4726
        }
4727
      }
4728
    }
4729
4730
    uint8_t leading_byte = data[pos]; // leading byte
4731
    if (leading_byte < 0b10000000) {
4732
      // converting one ASCII byte !!!
4733
      *utf16_output++ = !match_system(big_endian)
4734
                            ? char16_t(u16_swap_bytes(leading_byte))
4735
                            : char16_t(leading_byte);
4736
      pos++;
4737
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
4738
      // We have a two-byte UTF-8, it should become
4739
      // a single UTF-16 word.
4740
      if (pos + 1 >= len) {
4741
        return 0;
4742
      } // minimal bound checking
4743
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4744
        return 0;
4745
      }
4746
      // range check
4747
      uint32_t code_point =
4748
          (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
4749
      if (code_point < 0x80) {
4750
        return 0;
4751
      }
4752
      if constexpr (!match_system(big_endian)) {
4753
        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
4754
      }
4755
      *utf16_output++ = char16_t(code_point);
4756
      pos += 2;
4757
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
4758
      // We have a three-byte UTF-8, it should become
4759
      // a single UTF-16 word.
4760
      if (pos + 2 >= len) {
4761
        return 0;
4762
      } // minimal bound checking
4763
4764
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4765
        return 0;
4766
      }
4767
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4768
        return 0;
4769
      }
4770
      // range check
4771
      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
4772
                            (data[pos + 1] & 0b00111111) << 6 |
4773
                            (data[pos + 2] & 0b00111111);
4774
      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
4775
        return 0;
4776
      }
4777
      if constexpr (!match_system(big_endian)) {
4778
        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
4779
      }
4780
      *utf16_output++ = char16_t(code_point);
4781
      pos += 3;
4782
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
4783
      // we have a 4-byte UTF-8 word.
4784
      if (pos + 3 >= len) {
4785
        return 0;
4786
      } // minimal bound checking
4787
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
4788
        return 0;
4789
      }
4790
      if ((data[pos + 2] & 0b11000000) != 0b10000000) {
4791
        return 0;
4792
      }
4793
      if ((data[pos + 3] & 0b11000000) != 0b10000000) {
4794
        return 0;
4795
      }
4796
4797
      // range check
4798
      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
4799
                            (data[pos + 1] & 0b00111111) << 12 |
4800
                            (data[pos + 2] & 0b00111111) << 6 |
4801
                            (data[pos + 3] & 0b00111111);
4802
      if (code_point <= 0xffff || 0x10ffff < code_point) {
4803
        return 0;
4804
      }
4805
      code_point -= 0x10000;
4806
      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
4807
      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
4808
      // the shift, which drops bits 16-19 of the code point.
4809
      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
4810
      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
4811
      if constexpr (!match_system(big_endian)) {
4812
        high_surrogate = u16_swap_bytes(high_surrogate);
4813
        low_surrogate = u16_swap_bytes(low_surrogate);
4814
      }
4815
      *utf16_output++ = char16_t(high_surrogate);
4816
      *utf16_output++ = char16_t(low_surrogate);
4817
      pos += 4;
4818
    } else {
4819
      return 0;
4820
    }
4821
  }
4822
  return utf16_output - start;
4823
}
4824
4825
template <endianness big_endian, typename InputPtr>
4826
#if SIMDUTF_CPLUSPLUS20
4827
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
4828
#endif
4829
simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
4830
                                               char16_t *utf16_output) {
4831
  size_t pos = 0;
4832
  char16_t *start{utf16_output};
4833
  while (pos < len) {
4834
#if SIMDUTF_CPLUSPLUS23
4835
    if !consteval
4836
#endif
4837
    {
4838
      // try to convert the next block of 16 ASCII bytes
4839
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
4840
                             // they are ascii
4841
        uint64_t v1;
4842
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
4843
        uint64_t v2;
4844
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
4845
        uint64_t v{v1 | v2};
4846
        if ((v & 0x8080808080808080) == 0) {
4847
          size_t final_pos = pos + 16;
4848
          while (pos < final_pos) {
4849
            const char16_t byte = uint8_t(data[pos]);
4850
            *utf16_output++ =
4851
                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
4852
            pos++;
4853
          }
4854
          continue;
4855
        }
4856
      }
4857
    }
4858
4859
    auto leading_byte = uint8_t(data[pos]); // leading byte
4860
    if (leading_byte < 0b10000000) {
4861
      // converting one ASCII byte !!!
4862
      *utf16_output++ = !match_system(big_endian)
4863
                            ? char16_t(u16_swap_bytes(leading_byte))
4864
                            : char16_t(leading_byte);
4865
      pos++;
4866
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
4867
      // We have a two-byte UTF-8, it should become
4868
      // a single UTF-16 word.
4869
      if (pos + 1 >= len) {
4870
        return result(error_code::TOO_SHORT, pos);
4871
      } // minimal bound checking
4872
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
4873
        return result(error_code::TOO_SHORT, pos);
4874
      }
4875
      // range check
4876
      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
4877
                            (uint8_t(data[pos + 1]) & 0b00111111);
4878
      if (code_point < 0x80) {
4879
        return result(error_code::OVERLONG, pos);
4880
      }
4881
      if constexpr (!match_system(big_endian)) {
4882
        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
4883
      }
4884
      *utf16_output++ = char16_t(code_point);
4885
      pos += 2;
4886
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
4887
      // We have a three-byte UTF-8, it should become
4888
      // a single UTF-16 word.
4889
      if (pos + 2 >= len) {
4890
        return result(error_code::TOO_SHORT, pos);
4891
      } // minimal bound checking
4892
4893
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
4894
        return result(error_code::TOO_SHORT, pos);
4895
      }
4896
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
4897
        return result(error_code::TOO_SHORT, pos);
4898
      }
4899
      // range check
4900
      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
4901
                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
4902
                            (uint8_t(data[pos + 2]) & 0b00111111);
4903
      if (code_point < 0x800) {
4904
        return result(error_code::OVERLONG, pos);
4905
      }
4906
      if (0xd7ff < code_point && code_point < 0xe000) {
4907
        return result(error_code::SURROGATE, pos);
4908
      }
4909
      if constexpr (!match_system(big_endian)) {
4910
        code_point = uint32_t(u16_swap_bytes(uint16_t(code_point)));
4911
      }
4912
      *utf16_output++ = char16_t(code_point);
4913
      pos += 3;
4914
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
4915
      // we have a 4-byte UTF-8 word.
4916
      if (pos + 3 >= len) {
4917
        return result(error_code::TOO_SHORT, pos);
4918
      } // minimal bound checking
4919
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
4920
        return result(error_code::TOO_SHORT, pos);
4921
      }
4922
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
4923
        return result(error_code::TOO_SHORT, pos);
4924
      }
4925
      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
4926
        return result(error_code::TOO_SHORT, pos);
4927
      }
4928
4929
      // range check
4930
      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
4931
                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
4932
                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
4933
                            (uint8_t(data[pos + 3]) & 0b00111111);
4934
      if (code_point <= 0xffff) {
4935
        return result(error_code::OVERLONG, pos);
4936
      }
4937
      if (0x10ffff < code_point) {
4938
        return result(error_code::TOO_LARGE, pos);
4939
      }
4940
      code_point -= 0x10000;
4941
      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
4942
      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
4943
      // the shift, which drops bits 16-19 of the code point.
4944
      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
4945
      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
4946
      if constexpr (!match_system(big_endian)) {
4947
        high_surrogate = u16_swap_bytes(high_surrogate);
4948
        low_surrogate = u16_swap_bytes(low_surrogate);
4949
      }
4950
      *utf16_output++ = char16_t(high_surrogate);
4951
      *utf16_output++ = char16_t(low_surrogate);
4952
      pos += 4;
4953
    } else {
4954
      // we either have too many continuation bytes or an invalid leading byte
4955
      if ((leading_byte & 0b11000000) == 0b10000000) {
4956
        return result(error_code::TOO_LONG, pos);
4957
      } else {
4958
        return result(error_code::HEADER_BITS, pos);
4959
      }
4960
    }
4961
  }
4962
  return result(error_code::SUCCESS, utf16_output - start);
4963
}
4964
4965
/**
4966
 * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and
4967
 * we have up to len input bytes left, and we encountered some error. It is
4968
 * possible that the error is at 'buf' exactly, but it could also be in the
4969
 * previous bytes  (up to 3 bytes back).
4970
 *
4971
 * prior_bytes indicates how many bytes, prior to 'buf' may belong to the
4972
 * current memory section and can be safely accessed. We prior_bytes to access
4973
 * safely up to three bytes before 'buf'.
4974
 *
4975
 * The caller is responsible to ensure that len > 0.
4976
 *
4977
 * If the error is believed to have occurred prior to 'buf', the count value
4978
 * contain in the result will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3.
4979
 */
4980
template <endianness endian>
4981
inline result rewind_and_convert_with_errors(size_t prior_bytes,
4982
                                             const char *buf, size_t len,
4983
                                             char16_t *utf16_output) {
4984
  size_t extra_len{0};
4985
  // We potentially need to go back in time and find a leading byte.
4986
  // In theory '3' would be sufficient, but sometimes the error can go back
4987
  // quite far.
4988
  size_t how_far_back = prior_bytes;
4989
  // size_t how_far_back = 3; // 3 bytes in the past + current position
4990
  // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
4991
  bool found_leading_bytes{false};
4992
  // important: it is i <= how_far_back and not 'i < how_far_back'.
4993
  for (size_t i = 0; i <= how_far_back; i++) {
4994
    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
4995
    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
4996
    if (found_leading_bytes) {
4997
      if (i > 0 && byte < 128) {
4998
        // If we had to go back and the leading byte is ascii
4999
        // then we can stop right away.
5000
        return result(error_code::TOO_LONG, 0 - i + 1);
5001
      }
5002
      buf -= i;
5003
      extra_len = i;
5004
      break;
5005
    }
5006
  }
5007
  //
5008
  // It is possible for this function to return a negative count in its result.
5009
  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
5010
  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
5011
  // unsigned integral type of the result of the sizeof operator
5012
  //
5013
  // An unsigned type will simply wrap round arithmetically (well defined).
5014
  //
5015
  if (!found_leading_bytes) {
5016
    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
5017
    // [....] [continuation] [continuation] [continuation] | [buf is
5018
    // continuation] Or we possibly have a stream that does not start with a
5019
    // leading byte.
5020
    return result(error_code::TOO_LONG, 0 - how_far_back);
5021
  }
5022
  result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
5023
  if (res.error) {
5024
    res.count -= extra_len;
5025
  }
5026
  return res;
5027
}
5028
5029
} // namespace utf8_to_utf16
5030
} // unnamed namespace
5031
} // namespace scalar
5032
} // namespace simdutf
5033
5034
#endif
5035
/* end file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16.h */
5036
/* begin file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16_with_replacement.h */
5037
#ifndef SIMDUTF_UTF8_TO_UTF16_WITH_REPLACEMENT_H
5038
#define SIMDUTF_UTF8_TO_UTF16_WITH_REPLACEMENT_H
5039
5040
#include <cstring>
5041
5042
namespace simdutf {
5043
namespace scalar {
5044
namespace {
5045
namespace utf8_to_utf16 {
5046
5047
// Pointer-like view so validate_with_counts can run during constant
5048
// evaluation, where reinterpret_cast from char* to uint8_t* is not allowed.
5049
// operator+ exists only for the runtime ASCII probe inside that function.
5050
template <typename InputPtr> struct utf8_byte_pointer {
5051
  InputPtr p;
5052
  simdutf_constexpr23 uint8_t operator*() const noexcept {
5053
    return uint8_t(static_cast<unsigned char>(*p));
5054
  }
5055
  simdutf_constexpr23 uint8_t operator[](size_t i) const noexcept {
5056
    return uint8_t(static_cast<unsigned char>(p[i]));
5057
  }
5058
  const uint8_t *operator+(size_t i) const noexcept {
5059
    return reinterpret_cast<const uint8_t *>(static_cast<const void *>(p + i));
5060
  }
5061
};
5062
5063
// Byte length of one maximal subpart starting at ptr. n must be at least 1.
5064
// The validator reports the first byte of the ill-formed sequence.
5065
template <typename InputPtr>
5066
#if SIMDUTF_CPLUSPLUS20
5067
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5068
#endif
5069
simdutf_constexpr23 size_t maximal_subpart(InputPtr ptr, size_t n) noexcept {
5070
  const uint8_t b = uint8_t(static_cast<unsigned char>(ptr[0]));
5071
  size_t need;
5072
  uint8_t lo = 0x80;
5073
  uint8_t hi = 0xBF;
5074
  if (b < 0xC2) {
5075
    return 1; // ASCII is handled by the caller; continuation, C0, C1
5076
  } else if (b < 0xE0) {
5077
    need = 1;
5078
  } else if (b < 0xF0) {
5079
    need = 2;
5080
    if (b == 0xE0) {
5081
      lo = 0xA0;
5082
    } else if (b == 0xED) {
5083
      hi = 0x9F;
5084
    }
5085
  } else if (b < 0xF5) {
5086
    need = 3;
5087
    if (b == 0xF0) {
5088
      lo = 0x90;
5089
    } else if (b == 0xF4) {
5090
      hi = 0x8F;
5091
    }
5092
  } else {
5093
    return 1; // F5..FF
5094
  }
5095
  if (n < 2 || uint8_t(static_cast<unsigned char>(ptr[1])) < lo ||
5096
      uint8_t(static_cast<unsigned char>(ptr[1])) > hi) {
5097
    return 1;
5098
  }
5099
  size_t i = 2;
5100
  while (i <= need && i < n &&
5101
         (uint8_t(static_cast<unsigned char>(ptr[i])) & 0xC0) == 0x80) {
5102
    i++;
5103
  }
5104
  return i;
5105
}
5106
5107
// WHATWG / Unicode substitution of maximal subparts. One U+FFFD per subpart.
5108
// `write == false` counts UTF-16 code units and does not store.
5109
template <endianness endian, bool write, typename InputPtr>
5110
#if SIMDUTF_CPLUSPLUS20
5111
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5112
#endif
5113
simdutf_constexpr23 size_t transcode_with_replacement(
5114
    InputPtr data, size_t len, char16_t *utf16_output) noexcept {
5115
  size_t i = 0;
5116
  size_t written = 0;
5117
  uint32_t cp = 0;
5118
  size_t needed = 0;
5119
  size_t seen = 0;
5120
  uint8_t lo = 0x80;
5121
  uint8_t hi = 0xBF;
5122
  auto store = [&](uint32_t c) {
5123
    if (c >= 0x10000) {
5124
      c -= 0x10000;
5125
      // The mask is a no-op (c < 0x100000). It stops clang 18's SLP
5126
      // vectorizer on RISC-V V from narrowing c to 16 bits before
5127
      // the shift, which drops bits 16-19 of the code point.
5128
      const uint16_t high = uint16_t(0xD800 + ((c >> 10) & 0x3FF));
5129
      const uint16_t low = uint16_t(0xDC00 + (c & 0x3FF));
5130
      if constexpr (write) {
5131
        utf16_output[written] =
5132
            char16_t(scalar::utf16::swap_if_needed<endian>(high));
5133
        utf16_output[written + 1] =
5134
            char16_t(scalar::utf16::swap_if_needed<endian>(low));
5135
      }
5136
      written += 2;
5137
    } else {
5138
      if constexpr (write) {
5139
        utf16_output[written] =
5140
            char16_t(scalar::utf16::swap_if_needed<endian>(uint16_t(c)));
5141
      }
5142
      written += 1;
5143
    }
5144
  };
5145
  while (i < len) {
5146
    if (needed == 0 && i + 16 <= len) {
5147
#if SIMDUTF_CPLUSPLUS23
5148
      if !consteval
5149
#endif
5150
      {
5151
        uint64_t v1;
5152
        std::memcpy(&v1, data + i, sizeof(v1));
5153
        uint64_t v2;
5154
        std::memcpy(&v2, data + i + sizeof(v1), sizeof(v2));
5155
        if (((v1 | v2) & 0x8080808080808080ULL) == 0) {
5156
          for (size_t k = 0; k < 16; k++) {
5157
            store(uint8_t(static_cast<unsigned char>(data[i + k])));
5158
          }
5159
          i += 16;
5160
          continue;
5161
        }
5162
      }
5163
    }
5164
    const uint8_t b = uint8_t(static_cast<unsigned char>(data[i]));
5165
    if (needed == 0) {
5166
      i++;
5167
      lo = 0x80;
5168
      hi = 0xBF;
5169
      if (b < 0x80) {
5170
        store(b);
5171
      } else if (b <= 0xDF) {
5172
        if (b < 0xC2) {
5173
          store(0xFFFD);
5174
        } else {
5175
          needed = 1;
5176
          cp = b & 0x1F;
5177
        }
5178
      } else if (b <= 0xEF) {
5179
        if (b == 0xE0) {
5180
          lo = 0xA0;
5181
        } else if (b == 0xED) {
5182
          hi = 0x9F;
5183
        }
5184
        needed = 2;
5185
        cp = b & 0x0F;
5186
      } else if (b <= 0xF4) {
5187
        if (b == 0xF0) {
5188
          lo = 0x90;
5189
        } else if (b == 0xF4) {
5190
          hi = 0x8F;
5191
        }
5192
        needed = 3;
5193
        cp = b & 0x07;
5194
      } else {
5195
        store(0xFFFD);
5196
      }
5197
      continue;
5198
    }
5199
    if (b < lo || b > hi) {
5200
      needed = 0;
5201
      seen = 0;
5202
      cp = 0;
5203
      lo = 0x80;
5204
      hi = 0xBF;
5205
      store(0xFFFD);
5206
      continue;
5207
    }
5208
    i++;
5209
    lo = 0x80;
5210
    hi = 0xBF;
5211
    cp = (cp << 6) | uint32_t(b & 0x3F);
5212
    seen++;
5213
    if (seen == needed) {
5214
      store(cp);
5215
      needed = 0;
5216
      seen = 0;
5217
      cp = 0;
5218
    }
5219
  }
5220
  if (needed != 0) {
5221
    store(0xFFFD);
5222
  }
5223
  return written;
5224
}
5225
5226
template <endianness endian, typename InputPtr>
5227
#if SIMDUTF_CPLUSPLUS20
5228
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5229
#endif
5230
simdutf_constexpr23 size_t convert_with_replacement(
5231
    InputPtr data, size_t len, char16_t *utf16_output) noexcept {
5232
  return transcode_with_replacement<endian, true>(data, len, utf16_output);
5233
}
5234
5235
template <typename InputPtr>
5236
#if SIMDUTF_CPLUSPLUS20
5237
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5238
#endif
5239
simdutf_constexpr23 size_t count_with_replacement(InputPtr data,
5240
                                                  size_t len) noexcept {
5241
  return transcode_with_replacement<endianness::LITTLE, false>(data, len,
5242
                                                               nullptr);
5243
}
5244
5245
template <typename InputPtr>
5246
#if SIMDUTF_CPLUSPLUS20
5247
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5248
#endif
5249
simdutf_constexpr23 utf8_to_utf16_result
5250
utf16_length_from_utf8_with_replacement(InputPtr data, size_t len) noexcept {
5251
  utf8_to_utf16_result out;
5252
  size_t pos = 0;
5253
  while (pos < len) {
5254
    utf8_result validation;
5255
#if SIMDUTF_CPLUSPLUS23
5256
    if consteval {
5257
      validation = scalar::utf8::validate_with_counts(
5258
          utf8_byte_pointer<InputPtr>{data + pos}, len - pos);
5259
    } else
5260
#endif
5261
    {
5262
      validation = scalar::utf8::validate_with_counts(
5263
          reinterpret_cast<const uint8_t *>(data + pos), len - pos);
5264
    }
5265
    if (validation.error == error_code::SUCCESS) {
5266
      out.count += validation.utf16_length();
5267
      return out;
5268
    }
5269
    if (validation.input_count >= len - pos) {
5270
      out.count += count_with_replacement(data + pos, len - pos);
5271
      out.more_errors = true;
5272
      if (out.error == error_code::SUCCESS) {
5273
        out.error = validation.error;
5274
      }
5275
      return out;
5276
    }
5277
    out.count += validation.utf16_length() + 1;
5278
    if (out.error == error_code::SUCCESS) {
5279
      out.error = validation.error;
5280
    }
5281
    const size_t err = pos + validation.input_count;
5282
    const size_t skip = maximal_subpart(data + err, len - err);
5283
    if (out.error_count < utf8_to_utf16_result::max_errors) {
5284
      out.error_offset[out.error_count] = err;
5285
      out.error_count += 1;
5286
    } else {
5287
      out.more_errors = true;
5288
    }
5289
    pos = err + skip;
5290
  }
5291
  return out;
5292
}
5293
5294
} // namespace utf8_to_utf16
5295
} // unnamed namespace
5296
} // namespace scalar
5297
} // namespace simdutf
5298
5299
#endif // SIMDUTF_UTF8_TO_UTF16_WITH_REPLACEMENT_H
5300
/* end file include/simdutf/scalar/utf8_to_utf16/utf8_to_utf16_with_replacement.h */
5301
/* begin file include/simdutf/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */
5302
#ifndef SIMDUTF_VALID_UTF8_TO_UTF16_H
5303
#define SIMDUTF_VALID_UTF8_TO_UTF16_H
5304
5305
#include <cstring>
5306
5307
namespace simdutf {
5308
namespace scalar {
5309
namespace {
5310
namespace utf8_to_utf16 {
5311
5312
template <endianness big_endian, typename InputPtr>
5313
#if SIMDUTF_CPLUSPLUS20
5314
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5315
#endif
5316
simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len,
5317
                                         char16_t *utf16_output) {
5318
  size_t pos = 0;
5319
  char16_t *start{utf16_output};
5320
  while (pos < len) {
5321
#if SIMDUTF_CPLUSPLUS23
5322
    if !consteval
5323
#endif
5324
    {                       // try to convert the next block of 8 ASCII bytes
5325
      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
5326
                            // they are ascii
5327
        uint64_t v;
5328
        ::memcpy(&v, data + pos, sizeof(uint64_t));
5329
        if ((v & 0x8080808080808080) == 0) {
5330
          size_t final_pos = pos + 8;
5331
          while (pos < final_pos) {
5332
            const char16_t byte = uint8_t(data[pos]);
5333
            *utf16_output++ =
5334
                !match_system(big_endian) ? u16_swap_bytes(byte) : byte;
5335
            pos++;
5336
          }
5337
          continue;
5338
        }
5339
      }
5340
    }
5341
5342
    auto leading_byte = uint8_t(data[pos]); // leading byte
5343
    if (leading_byte < 0b10000000) {
5344
      // converting one ASCII byte !!!
5345
      *utf16_output++ = !match_system(big_endian)
5346
                            ? char16_t(u16_swap_bytes(leading_byte))
5347
                            : char16_t(leading_byte);
5348
      pos++;
5349
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
5350
      // We have a two-byte UTF-8, it should become
5351
      // a single UTF-16 word.
5352
      if (pos + 1 >= len) {
5353
        break;
5354
      } // minimal bound checking
5355
      uint16_t code_point = uint16_t(((leading_byte & 0b00011111) << 6) |
5356
                                     (uint8_t(data[pos + 1]) & 0b00111111));
5357
      if constexpr (!match_system(big_endian)) {
5358
        code_point = u16_swap_bytes(uint16_t(code_point));
5359
      }
5360
      *utf16_output++ = char16_t(code_point);
5361
      pos += 2;
5362
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
5363
      // We have a three-byte UTF-8, it should become
5364
      // a single UTF-16 word.
5365
      if (pos + 2 >= len) {
5366
        break;
5367
      } // minimal bound checking
5368
      uint16_t code_point =
5369
          uint16_t(((leading_byte & 0b00001111) << 12) |
5370
                   ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
5371
                   (uint8_t(data[pos + 2]) & 0b00111111));
5372
      if constexpr (!match_system(big_endian)) {
5373
        code_point = u16_swap_bytes(uint16_t(code_point));
5374
      }
5375
      *utf16_output++ = char16_t(code_point);
5376
      pos += 3;
5377
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
5378
      // we have a 4-byte UTF-8 word.
5379
      if (pos + 3 >= len) {
5380
        break;
5381
      } // minimal bound checking
5382
      uint32_t code_point = ((leading_byte & 0b00000111) << 18) |
5383
                            ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
5384
                            ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
5385
                            (uint8_t(data[pos + 3]) & 0b00111111);
5386
      code_point -= 0x10000;
5387
      // The mask is a no-op (code_point < 0x100000). It stops clang 18's SLP
5388
      // vectorizer on RISC-V V from narrowing code_point to 16 bits before
5389
      // the shift, which drops bits 16-19 of the code point.
5390
      uint16_t high_surrogate = uint16_t(0xD800 + ((code_point >> 10) & 0x3FF));
5391
      uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
5392
      if constexpr (!match_system(big_endian)) {
5393
        high_surrogate = u16_swap_bytes(high_surrogate);
5394
        low_surrogate = u16_swap_bytes(low_surrogate);
5395
      }
5396
      *utf16_output++ = char16_t(high_surrogate);
5397
      *utf16_output++ = char16_t(low_surrogate);
5398
      pos += 4;
5399
    } else {
5400
      // we may have a continuation but we do not do error checking
5401
      return 0;
5402
    }
5403
  }
5404
  return utf16_output - start;
5405
}
5406
5407
} // namespace utf8_to_utf16
5408
} // unnamed namespace
5409
} // namespace scalar
5410
} // namespace simdutf
5411
5412
#endif
5413
/* end file include/simdutf/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */
5414
/* begin file include/simdutf/scalar/utf8_to_utf32/utf8_to_utf32.h */
5415
#ifndef SIMDUTF_UTF8_TO_UTF32_H
5416
#define SIMDUTF_UTF8_TO_UTF32_H
5417
5418
#include <cstring>
5419
5420
namespace simdutf {
5421
namespace scalar {
5422
namespace {
5423
namespace utf8_to_utf32 {
5424
5425
template <typename InputPtr>
5426
#if SIMDUTF_CPLUSPLUS20
5427
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5428
#endif
5429
simdutf_constexpr23 size_t convert(InputPtr data, size_t len,
5430
                                   char32_t *utf32_output) {
5431
  size_t pos = 0;
5432
  char32_t *start{utf32_output};
5433
  while (pos < len) {
5434
#if SIMDUTF_CPLUSPLUS23
5435
    if !consteval
5436
#endif
5437
    {
5438
      // try to convert the next block of 16 ASCII bytes
5439
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
5440
                             // they are ascii
5441
        uint64_t v1;
5442
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
5443
        uint64_t v2;
5444
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
5445
        uint64_t v{v1 | v2};
5446
        if ((v & 0x8080808080808080) == 0) {
5447
          size_t final_pos = pos + 16;
5448
          while (pos < final_pos) {
5449
            *utf32_output++ = uint8_t(data[pos]);
5450
            pos++;
5451
          }
5452
          continue;
5453
        }
5454
      }
5455
    }
5456
    auto leading_byte = uint8_t(data[pos]); // leading byte
5457
    if (leading_byte < 0b10000000) {
5458
      // converting one ASCII byte !!!
5459
      *utf32_output++ = char32_t(leading_byte);
5460
      pos++;
5461
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
5462
      // We have a two-byte UTF-8
5463
      if (pos + 1 >= len) {
5464
        return 0;
5465
      } // minimal bound checking
5466
      if ((data[pos + 1] & 0b11000000) != 0b10000000) {
5467
        return 0;
5468
      }
5469
      // range check
5470
      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
5471
                            (uint8_t(data[pos + 1]) & 0b00111111);
5472
      if (code_point < 0x80) {
5473
        return 0;
5474
      }
5475
      *utf32_output++ = char32_t(code_point);
5476
      pos += 2;
5477
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
5478
      // We have a three-byte UTF-8
5479
      if (pos + 2 >= len) {
5480
        return 0;
5481
      } // minimal bound checking
5482
5483
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
5484
        return 0;
5485
      }
5486
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
5487
        return 0;
5488
      }
5489
      // range check
5490
      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
5491
                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
5492
                            (uint8_t(data[pos + 2]) & 0b00111111);
5493
      if (code_point < 0x800 || (0xd7ff < code_point && code_point < 0xe000)) {
5494
        return 0;
5495
      }
5496
      *utf32_output++ = char32_t(code_point);
5497
      pos += 3;
5498
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
5499
      // we have a 4-byte UTF-8 word.
5500
      if (pos + 3 >= len) {
5501
        return 0;
5502
      } // minimal bound checking
5503
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
5504
        return 0;
5505
      }
5506
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
5507
        return 0;
5508
      }
5509
      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
5510
        return 0;
5511
      }
5512
5513
      // range check
5514
      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
5515
                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
5516
                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
5517
                            (uint8_t(data[pos + 3]) & 0b00111111);
5518
      if (code_point <= 0xffff || 0x10ffff < code_point) {
5519
        return 0;
5520
      }
5521
      *utf32_output++ = char32_t(code_point);
5522
      pos += 4;
5523
    } else {
5524
      return 0;
5525
    }
5526
  }
5527
  return utf32_output - start;
5528
}
5529
5530
template <typename InputPtr>
5531
#if SIMDUTF_CPLUSPLUS20
5532
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5533
#endif
5534
simdutf_constexpr23 result convert_with_errors(InputPtr data, size_t len,
5535
0
                                               char32_t *utf32_output) {
5536
0
  size_t pos = 0;
5537
0
  char32_t *start{utf32_output};
5538
0
  while (pos < len) {
5539
0
#if SIMDUTF_CPLUSPLUS23
5540
0
    if !consteval
5541
0
#endif
5542
0
    {
5543
0
      // try to convert the next block of 16 ASCII bytes
5544
0
      if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that
5545
0
                             // they are ascii
5546
0
        uint64_t v1;
5547
0
        ::memcpy(&v1, data + pos, sizeof(uint64_t));
5548
0
        uint64_t v2;
5549
0
        ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
5550
0
        uint64_t v{v1 | v2};
5551
0
        if ((v & 0x8080808080808080) == 0) {
5552
0
          size_t final_pos = pos + 16;
5553
0
          while (pos < final_pos) {
5554
0
            *utf32_output++ = uint8_t(data[pos]);
5555
0
            pos++;
5556
0
          }
5557
0
          continue;
5558
0
        }
5559
0
      }
5560
0
    }
5561
0
    auto leading_byte = uint8_t(data[pos]); // leading byte
5562
0
    if (leading_byte < 0b10000000) {
5563
0
      // converting one ASCII byte !!!
5564
0
      *utf32_output++ = char32_t(leading_byte);
5565
0
      pos++;
5566
0
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
5567
0
      // We have a two-byte UTF-8
5568
0
      if (pos + 1 >= len) {
5569
0
        return result(error_code::TOO_SHORT, pos);
5570
0
      } // minimal bound checking
5571
0
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
5572
0
        return result(error_code::TOO_SHORT, pos);
5573
0
      }
5574
0
      // range check
5575
0
      uint32_t code_point = (leading_byte & 0b00011111) << 6 |
5576
0
                            (uint8_t(data[pos + 1]) & 0b00111111);
5577
0
      if (code_point < 0x80) {
5578
0
        return result(error_code::OVERLONG, pos);
5579
0
      }
5580
0
      *utf32_output++ = char32_t(code_point);
5581
0
      pos += 2;
5582
0
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
5583
0
      // We have a three-byte UTF-8
5584
0
      if (pos + 2 >= len) {
5585
0
        return result(error_code::TOO_SHORT, pos);
5586
0
      } // minimal bound checking
5587
0
5588
0
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
5589
0
        return result(error_code::TOO_SHORT, pos);
5590
0
      }
5591
0
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
5592
0
        return result(error_code::TOO_SHORT, pos);
5593
0
      }
5594
0
      // range check
5595
0
      uint32_t code_point = (leading_byte & 0b00001111) << 12 |
5596
0
                            (uint8_t(data[pos + 1]) & 0b00111111) << 6 |
5597
0
                            (uint8_t(data[pos + 2]) & 0b00111111);
5598
0
      if (code_point < 0x800) {
5599
0
        return result(error_code::OVERLONG, pos);
5600
0
      }
5601
0
      if (0xd7ff < code_point && code_point < 0xe000) {
5602
0
        return result(error_code::SURROGATE, pos);
5603
0
      }
5604
0
      *utf32_output++ = char32_t(code_point);
5605
0
      pos += 3;
5606
0
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
5607
0
      // we have a 4-byte UTF-8 word.
5608
0
      if (pos + 3 >= len) {
5609
0
        return result(error_code::TOO_SHORT, pos);
5610
0
      } // minimal bound checking
5611
0
      if ((uint8_t(data[pos + 1]) & 0b11000000) != 0b10000000) {
5612
0
        return result(error_code::TOO_SHORT, pos);
5613
0
      }
5614
0
      if ((uint8_t(data[pos + 2]) & 0b11000000) != 0b10000000) {
5615
0
        return result(error_code::TOO_SHORT, pos);
5616
0
      }
5617
0
      if ((uint8_t(data[pos + 3]) & 0b11000000) != 0b10000000) {
5618
0
        return result(error_code::TOO_SHORT, pos);
5619
0
      }
5620
0
5621
0
      // range check
5622
0
      uint32_t code_point = (leading_byte & 0b00000111) << 18 |
5623
0
                            (uint8_t(data[pos + 1]) & 0b00111111) << 12 |
5624
0
                            (uint8_t(data[pos + 2]) & 0b00111111) << 6 |
5625
0
                            (uint8_t(data[pos + 3]) & 0b00111111);
5626
0
      if (code_point <= 0xffff) {
5627
0
        return result(error_code::OVERLONG, pos);
5628
0
      }
5629
0
      if (0x10ffff < code_point) {
5630
0
        return result(error_code::TOO_LARGE, pos);
5631
0
      }
5632
0
      *utf32_output++ = char32_t(code_point);
5633
0
      pos += 4;
5634
0
    } else {
5635
0
      // we either have too many continuation bytes or an invalid leading byte
5636
0
      if ((leading_byte & 0b11000000) == 0b10000000) {
5637
0
        return result(error_code::TOO_LONG, pos);
5638
0
      } else {
5639
0
        return result(error_code::HEADER_BITS, pos);
5640
0
      }
5641
0
    }
5642
0
  }
5643
0
  return result(error_code::SUCCESS, utf32_output - start);
5644
0
}
Unexecuted instantiation: parser.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3219convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPDi
Unexecuted instantiation: base64.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3219convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPDi
Unexecuted instantiation: simdutf.cpp:_ZN7simdutf6scalar12_GLOBAL__N_113utf8_to_utf3219convert_with_errorsIPKcQsr7simdutf6detailE22indexes_into_byte_likeIT_EEENS_6resultES6_mPDi
5645
5646
/**
5647
 * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and
5648
 * we have up to len input bytes left, and we encountered some error. It is
5649
 * possible that the error is at 'buf' exactly, but it could also be in the
5650
 * previous bytes location (up to 3 bytes back).
5651
 *
5652
 * prior_bytes indicates how many bytes, prior to 'buf' may belong to the
5653
 * current memory section and can be safely accessed. We prior_bytes to access
5654
 * safely up to three bytes before 'buf'.
5655
 *
5656
 * The caller is responsible to ensure that len > 0.
5657
 *
5658
 * If the error is believed to have occurred prior to 'buf', the count value
5659
 * contain in the result will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3.
5660
 */
5661
inline result rewind_and_convert_with_errors(size_t prior_bytes,
5662
                                             const char *buf, size_t len,
5663
0
                                             char32_t *utf32_output) {
5664
0
  size_t extra_len{0};
5665
0
  // We potentially need to go back in time and find a leading byte.
5666
0
  size_t how_far_back = 3; // 3 bytes in the past + current position
5667
0
  if (how_far_back > prior_bytes) {
5668
0
    how_far_back = prior_bytes;
5669
0
  }
5670
0
  bool found_leading_bytes{false};
5671
0
  // important: it is i <= how_far_back and not 'i < how_far_back'.
5672
0
  for (size_t i = 0; i <= how_far_back; i++) {
5673
0
    unsigned char byte = buf[-static_cast<std::ptrdiff_t>(i)];
5674
0
    found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
5675
0
    if (found_leading_bytes) {
5676
0
      if (i > 0 && byte < 128) {
5677
0
        // If we had to go back and the leading byte is ascii
5678
0
        // then we can stop right away.
5679
0
        return result(error_code::TOO_LONG, 0 - i + 1);
5680
0
      }
5681
0
      buf -= i;
5682
0
      extra_len = i;
5683
0
      break;
5684
0
    }
5685
0
  }
5686
0
  //
5687
0
  // It is possible for this function to return a negative count in its result.
5688
0
  // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described
5689
0
  // in C Standard as <stddef.h>. C Standard Section 4.1.5 defines size_t as an
5690
0
  // unsigned integral type of the result of the sizeof operator
5691
0
  //
5692
0
  // An unsigned type will simply wrap round arithmetically (well defined).
5693
0
  //
5694
0
  if (!found_leading_bytes) {
5695
0
    // If how_far_back == 3, we may have four consecutive continuation bytes!!!
5696
0
    // [....] [continuation] [continuation] [continuation] | [buf is
5697
0
    // continuation] Or we possibly have a stream that does not start with a
5698
0
    // leading byte.
5699
0
    return result(error_code::TOO_LONG, 0 - how_far_back);
5700
0
  }
5701
0
5702
0
  result res = convert_with_errors(buf, len + extra_len, utf32_output);
5703
0
  if (res.error) {
5704
0
    res.count -= extra_len;
5705
0
  }
5706
0
  return res;
5707
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_utf32::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char32_t*)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_utf32::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char32_t*)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::utf8_to_utf32::rewind_and_convert_with_errors(unsigned long, char const*, unsigned long, char32_t*)
5708
5709
} // namespace utf8_to_utf32
5710
} // unnamed namespace
5711
} // namespace scalar
5712
} // namespace simdutf
5713
5714
#endif
5715
/* end file include/simdutf/scalar/utf8_to_utf32/utf8_to_utf32.h */
5716
/* begin file include/simdutf/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */
5717
#ifndef SIMDUTF_VALID_UTF8_TO_UTF32_H
5718
#define SIMDUTF_VALID_UTF8_TO_UTF32_H
5719
5720
#include <cstring>
5721
5722
namespace simdutf {
5723
namespace scalar {
5724
namespace {
5725
namespace utf8_to_utf32 {
5726
5727
template <typename InputPtr>
5728
#if SIMDUTF_CPLUSPLUS20
5729
  requires simdutf::detail::indexes_into_byte_like<InputPtr>
5730
#endif
5731
simdutf_constexpr23 size_t convert_valid(InputPtr data, size_t len,
5732
                                         char32_t *utf32_output) {
5733
  size_t pos = 0;
5734
  char32_t *start{utf32_output};
5735
  while (pos < len) {
5736
#if SIMDUTF_CPLUSPLUS23
5737
    if !consteval
5738
#endif
5739
    {
5740
      // try to convert the next block of 8 ASCII bytes
5741
      if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that
5742
                            // they are ascii
5743
        uint64_t v;
5744
        ::memcpy(&v, data + pos, sizeof(uint64_t));
5745
        if ((v & 0x8080808080808080) == 0) {
5746
          size_t final_pos = pos + 8;
5747
          while (pos < final_pos) {
5748
            *utf32_output++ = uint8_t(data[pos]);
5749
            pos++;
5750
          }
5751
          continue;
5752
        }
5753
      }
5754
    }
5755
    auto leading_byte = uint8_t(data[pos]); // leading byte
5756
    if (leading_byte < 0b10000000) {
5757
      // converting one ASCII byte !!!
5758
      *utf32_output++ = char32_t(leading_byte);
5759
      pos++;
5760
    } else if ((leading_byte & 0b11100000) == 0b11000000) {
5761
      // We have a two-byte UTF-8
5762
      if (pos + 1 >= len) {
5763
        break;
5764
      } // minimal bound checking
5765
      *utf32_output++ = char32_t(((leading_byte & 0b00011111) << 6) |
5766
                                 (uint8_t(data[pos + 1]) & 0b00111111));
5767
      pos += 2;
5768
    } else if ((leading_byte & 0b11110000) == 0b11100000) {
5769
      // We have a three-byte UTF-8
5770
      if (pos + 2 >= len) {
5771
        break;
5772
      } // minimal bound checking
5773
      *utf32_output++ = char32_t(((leading_byte & 0b00001111) << 12) |
5774
                                 ((uint8_t(data[pos + 1]) & 0b00111111) << 6) |
5775
                                 (uint8_t(data[pos + 2]) & 0b00111111));
5776
      pos += 3;
5777
    } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
5778
      // we have a 4-byte UTF-8 word.
5779
      if (pos + 3 >= len) {
5780
        break;
5781
      } // minimal bound checking
5782
      uint32_t code_word = ((leading_byte & 0b00000111) << 18) |
5783
                           ((uint8_t(data[pos + 1]) & 0b00111111) << 12) |
5784
                           ((uint8_t(data[pos + 2]) & 0b00111111) << 6) |
5785
                           (uint8_t(data[pos + 3]) & 0b00111111);
5786
      *utf32_output++ = char32_t(code_word);
5787
      pos += 4;
5788
    } else {
5789
      // we may have a continuation but we do not do error checking
5790
      return 0;
5791
    }
5792
  }
5793
  return utf32_output - start;
5794
}
5795
5796
} // namespace utf8_to_utf32
5797
} // unnamed namespace
5798
} // namespace scalar
5799
} // namespace simdutf
5800
5801
#endif
5802
/* end file include/simdutf/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */
5803
5804
namespace simdutf {
5805
5806
constexpr size_t default_line_length =
5807
    76; ///< default line length for base64 encoding with lines
5808
5809
#if SIMDUTF_FEATURE_DETECT_ENCODING
5810
/**
5811
 * Autodetect the encoding of the input, a single encoding is recommended.
5812
 * E.g., the function might return simdutf::encoding_type::UTF8,
5813
 * simdutf::encoding_type::UTF16_LE, simdutf::encoding_type::UTF16_BE, or
5814
 * simdutf::encoding_type::UTF32_LE.
5815
 *
5816
 * @param input the string to analyze.
5817
 * @param length the length of the string in bytes.
5818
 * @return the detected encoding type
5819
 */
5820
simdutf_warn_unused simdutf::encoding_type
5821
autodetect_encoding(const char *input, size_t length) noexcept;
5822
simdutf_really_inline simdutf_warn_unused simdutf::encoding_type
5823
autodetect_encoding(const uint8_t *input, size_t length) noexcept {
5824
  return autodetect_encoding(reinterpret_cast<const char *>(input), length);
5825
}
5826
  #if SIMDUTF_SPAN
5827
/**
5828
 * Autodetect the encoding of the input, a single encoding is recommended.
5829
 * E.g., the function might return simdutf::encoding_type::UTF8,
5830
 * simdutf::encoding_type::UTF16_LE, simdutf::encoding_type::UTF16_BE, or
5831
 * simdutf::encoding_type::UTF32_LE.
5832
 *
5833
 * @param input the string to analyze. can be a anything span-like that has a
5834
 * data() and size() that points to character data: std::string,
5835
 * std::string_view, std::vector<char>, std::span<const std::byte> etc.
5836
 * @return the detected encoding type
5837
 */
5838
simdutf_really_inline simdutf_warn_unused simdutf::encoding_type
5839
autodetect_encoding(
5840
    const detail::input_span_of_byte_like auto &input) noexcept {
5841
  return autodetect_encoding(reinterpret_cast<const char *>(input.data()),
5842
                             input.size());
5843
}
5844
  #endif // SIMDUTF_SPAN
5845
5846
/**
5847
 * Autodetect the possible encodings of the input in one pass.
5848
 * E.g., if the input might be UTF-16LE or UTF-8, this function returns
5849
 * the value (simdutf::encoding_type::UTF8 | simdutf::encoding_type::UTF16_LE).
5850
 *
5851
 * Overridden by each implementation.
5852
 *
5853
 * @param input the string to analyze.
5854
 * @param length the length of the string in bytes.
5855
 * @return the detected encoding type
5856
 */
5857
simdutf_warn_unused int detect_encodings(const char *input,
5858
                                         size_t length) noexcept;
5859
simdutf_really_inline simdutf_warn_unused int
5860
detect_encodings(const uint8_t *input, size_t length) noexcept {
5861
  return detect_encodings(reinterpret_cast<const char *>(input), length);
5862
}
5863
  #if SIMDUTF_SPAN
5864
simdutf_really_inline simdutf_warn_unused int
5865
detect_encodings(const detail::input_span_of_byte_like auto &input) noexcept {
5866
  return detect_encodings(reinterpret_cast<const char *>(input.data()),
5867
                          input.size());
5868
}
5869
  #endif // SIMDUTF_SPAN
5870
#endif   // SIMDUTF_FEATURE_DETECT_ENCODING
5871
5872
#if SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
5873
/**
5874
 * Validate the UTF-8 string. This function may be best when you expect
5875
 * the input to be almost always valid. Otherwise, consider using
5876
 * validate_utf8_with_errors.
5877
 *
5878
 * Overridden by each implementation.
5879
 *
5880
 * @param buf the UTF-8 string to validate.
5881
 * @param len the length of the string in bytes.
5882
 * @return true if and only if the string is valid UTF-8.
5883
 */
5884
simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept;
5885
  #if SIMDUTF_SPAN
5886
simdutf_constexpr23 simdutf_really_inline simdutf_warn_unused bool
5887
validate_utf8(const detail::input_span_of_byte_like auto &input) noexcept {
5888
    #if SIMDUTF_CPLUSPLUS23
5889
  if consteval {
5890
    return scalar::utf8::validate(
5891
        detail::constexpr_cast_ptr<uint8_t>(input.data()), input.size());
5892
  } else
5893
    #endif
5894
  {
5895
    return validate_utf8(reinterpret_cast<const char *>(input.data()),
5896
                         input.size());
5897
  }
5898
}
5899
  #endif // SIMDUTF_SPAN
5900
#endif   // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
5901
5902
#if SIMDUTF_FEATURE_UTF8
5903
/**
5904
 * Validate the UTF-8 string and stop on error.
5905
 *
5906
 * Overridden by each implementation.
5907
 *
5908
 * @param buf the UTF-8 string to validate.
5909
 * @param len the length of the string in bytes.
5910
 * @return a result pair struct (of type simdutf::result containing the two
5911
 * fields error and count) with an error code and either position of the error
5912
 * (in the input in code units) if any, or the number of code units validated if
5913
 * successful.
5914
 */
5915
simdutf_warn_unused result validate_utf8_with_errors(const char *buf,
5916
                                                     size_t len) noexcept;
5917
  #if SIMDUTF_SPAN
5918
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused result
5919
validate_utf8_with_errors(
5920
    const detail::input_span_of_byte_like auto &input) noexcept {
5921
    #if SIMDUTF_CPLUSPLUS23
5922
  if consteval {
5923
    return scalar::utf8::validate_with_errors(
5924
        detail::constexpr_cast_ptr<uint8_t>(input.data()), input.size());
5925
  } else
5926
    #endif
5927
  {
5928
    return validate_utf8_with_errors(
5929
        reinterpret_cast<const char *>(input.data()), input.size());
5930
  }
5931
}
5932
  #endif // SIMDUTF_SPAN
5933
#endif   // SIMDUTF_FEATURE_UTF8
5934
5935
#if SIMDUTF_FEATURE_UTF8
5936
/**
5937
 * Validate the UTF-8 string and stop on error, while counting the number of
5938
 * continuation bytes and of four-byte sequences in the valid prefix.
5939
 *
5940
 * Overridden by each implementation.
5941
 *
5942
 * These two counts are enough to derive, without a second pass over the input,
5943
 * both the number of code points (input_count - continuation_count) and the
5944
 * number of UTF-16 code units (input_count - continuation_count +
5945
 * four_byte_count) of the valid prefix. The utf32_length() and utf16_length()
5946
 * helpers return them.
5947
 *
5948
 * @param buf the UTF-8 string to validate.
5949
 * @param len the length of the string in bytes.
5950
 * @return a utf8_result struct with an error code, the number of bytes in the
5951
 * valid prefix (input_count), and the counts of continuation bytes and of
5952
 * four-byte sequences within it.
5953
 */
5954
simdutf_warn_unused utf8_result validate_utf8_with_counts(const char *buf,
5955
                                                          size_t len) noexcept;
5956
  #if SIMDUTF_SPAN
5957
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused utf8_result
5958
validate_utf8_with_counts(
5959
    const detail::input_span_of_byte_like auto &input) noexcept {
5960
    #if SIMDUTF_CPLUSPLUS23
5961
  if consteval {
5962
    return scalar::utf8::validate_with_counts(
5963
        detail::constexpr_cast_ptr<uint8_t>(input.data()), input.size());
5964
  } else
5965
    #endif
5966
  {
5967
    return validate_utf8_with_counts(
5968
        reinterpret_cast<const char *>(input.data()), input.size());
5969
  }
5970
}
5971
  #endif // SIMDUTF_SPAN
5972
#endif   // SIMDUTF_FEATURE_UTF8
5973
5974
#if SIMDUTF_FEATURE_ASCII
5975
/**
5976
 * Validate the ASCII string.
5977
 *
5978
 * Overridden by each implementation.
5979
 *
5980
 * @param buf the ASCII string to validate.
5981
 * @param len the length of the string in bytes.
5982
 * @return true if and only if the string is valid ASCII.
5983
 */
5984
simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept;
5985
  #if SIMDUTF_SPAN
5986
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
5987
validate_ascii(const detail::input_span_of_byte_like auto &input) noexcept {
5988
    #if SIMDUTF_CPLUSPLUS23
5989
  if consteval {
5990
    return scalar::ascii::validate(
5991
        detail::constexpr_cast_ptr<std::uint8_t>(input.data()), input.size());
5992
  } else
5993
    #endif
5994
  {
5995
    return validate_ascii(reinterpret_cast<const char *>(input.data()),
5996
                          input.size());
5997
  }
5998
}
5999
  #endif // SIMDUTF_SPAN
6000
6001
/**
6002
 * Validate the ASCII string and stop on error. It might be faster than
6003
 * validate_utf8 when an error is expected to occur early.
6004
 *
6005
 * Overridden by each implementation.
6006
 *
6007
 * @param buf the ASCII string to validate.
6008
 * @param len the length of the string in bytes.
6009
 * @return a result pair struct (of type simdutf::result containing the two
6010
 * fields error and count) with an error code and either position of the error
6011
 * (in the input in code units) if any, or the number of code units validated if
6012
 * successful.
6013
 */
6014
simdutf_warn_unused result validate_ascii_with_errors(const char *buf,
6015
                                                      size_t len) noexcept;
6016
  #if SIMDUTF_SPAN
6017
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6018
validate_ascii_with_errors(
6019
    const detail::input_span_of_byte_like auto &input) noexcept {
6020
    #if SIMDUTF_CPLUSPLUS23
6021
  if consteval {
6022
    return scalar::ascii::validate_with_errors(
6023
        detail::constexpr_cast_ptr<std::uint8_t>(input.data()), input.size());
6024
  } else
6025
    #endif
6026
  {
6027
    return validate_ascii_with_errors(
6028
        reinterpret_cast<const char *>(input.data()), input.size());
6029
  }
6030
}
6031
  #endif // SIMDUTF_SPAN
6032
#endif   // SIMDUTF_FEATURE_ASCII
6033
6034
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
6035
/**
6036
 * Validate the ASCII string as a UTF-16 sequence.
6037
 * An UTF-16 sequence is considered an ASCII sequence
6038
 * if it could be converted to an ASCII string losslessly.
6039
 *
6040
 * Overridden by each implementation.
6041
 *
6042
 * @param buf the UTF-16 string to validate.
6043
 * @param len the length of the string in bytes.
6044
 * @return true if and only if the string is valid ASCII.
6045
 */
6046
simdutf_warn_unused bool validate_utf16_as_ascii(const char16_t *buf,
6047
                                                 size_t len) noexcept;
6048
  #if SIMDUTF_SPAN
6049
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6050
validate_utf16_as_ascii(std::span<const char16_t> input) noexcept {
6051
    #if SIMDUTF_CPLUSPLUS23
6052
  if consteval {
6053
    return scalar::utf16::validate_as_ascii<endianness::NATIVE>(input.data(),
6054
                                                                input.size());
6055
  } else
6056
    #endif
6057
  {
6058
    return validate_utf16_as_ascii(input.data(), input.size());
6059
  }
6060
}
6061
  #endif // SIMDUTF_SPAN
6062
6063
/**
6064
 * Validate the ASCII string as a UTF-16BE sequence.
6065
 * An UTF-16 sequence is considered an ASCII sequence
6066
 * if it could be converted to an ASCII string losslessly.
6067
 *
6068
 * Overridden by each implementation.
6069
 *
6070
 * @param buf the UTF-16BE string to validate.
6071
 * @param len the length of the string in bytes.
6072
 * @return true if and only if the string is valid ASCII.
6073
 */
6074
simdutf_warn_unused bool validate_utf16be_as_ascii(const char16_t *buf,
6075
                                                   size_t len) noexcept;
6076
  #if SIMDUTF_SPAN
6077
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6078
validate_utf16be_as_ascii(std::span<const char16_t> input) noexcept {
6079
    #if SIMDUTF_CPLUSPLUS23
6080
  if consteval {
6081
    return scalar::utf16::validate_as_ascii<endianness::BIG>(input.data(),
6082
                                                             input.size());
6083
  } else
6084
    #endif
6085
  {
6086
    return validate_utf16be_as_ascii(input.data(), input.size());
6087
  }
6088
}
6089
  #endif // SIMDUTF_SPAN
6090
6091
/**
6092
 * Validate the ASCII string as a UTF-16LE sequence.
6093
 * An UTF-16 sequence is considered an ASCII sequence
6094
 * if it could be converted to an ASCII string losslessly.
6095
 *
6096
 * Overridden by each implementation.
6097
 *
6098
 * @param buf the UTF-16LE string to validate.
6099
 * @param len the length of the string in bytes.
6100
 * @return true if and only if the string is valid ASCII.
6101
 */
6102
simdutf_warn_unused bool validate_utf16le_as_ascii(const char16_t *buf,
6103
                                                   size_t len) noexcept;
6104
  #if SIMDUTF_SPAN
6105
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6106
validate_utf16le_as_ascii(std::span<const char16_t> input) noexcept {
6107
    #if SIMDUTF_CPLUSPLUS23
6108
  if consteval {
6109
    return scalar::utf16::validate_as_ascii<endianness::LITTLE>(input.data(),
6110
                                                                input.size());
6111
  } else
6112
    #endif
6113
  {
6114
    return validate_utf16le_as_ascii(input.data(), input.size());
6115
  }
6116
}
6117
  #endif // SIMDUTF_SPAN
6118
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
6119
6120
#if SIMDUTF_FEATURE_UTF16
6121
/**
6122
 * Using native endianness; Validate the UTF-16 string.
6123
 * This function may be best when you expect the input to be almost always
6124
 * valid. Otherwise, consider using validate_utf16_with_errors.
6125
 *
6126
 * Overridden by each implementation.
6127
 *
6128
 * This function is not BOM-aware.
6129
 *
6130
 * @param buf the UTF-16 string to validate.
6131
 * @param len the length of the string in number of 2-byte code units
6132
 * (char16_t).
6133
 * @return true if and only if the string is valid UTF-16.
6134
 */
6135
simdutf_warn_unused bool validate_utf16(const char16_t *buf,
6136
                                        size_t len) noexcept;
6137
  #if SIMDUTF_SPAN
6138
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6139
validate_utf16(std::span<const char16_t> input) noexcept {
6140
    #if SIMDUTF_CPLUSPLUS23
6141
  if consteval {
6142
    return scalar::utf16::validate<endianness::NATIVE>(input.data(),
6143
                                                       input.size());
6144
  } else
6145
    #endif
6146
  {
6147
    return validate_utf16(input.data(), input.size());
6148
  }
6149
}
6150
  #endif // SIMDUTF_SPAN
6151
#endif   // SIMDUTF_FEATURE_UTF16
6152
6153
#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
6154
/**
6155
 * Validate the UTF-16LE string. This function may be best when you expect
6156
 * the input to be almost always valid. Otherwise, consider using
6157
 * validate_utf16le_with_errors.
6158
 *
6159
 * Overridden by each implementation.
6160
 *
6161
 * This function is not BOM-aware.
6162
 *
6163
 * @param buf the UTF-16LE string to validate.
6164
 * @param len the length of the string in number of 2-byte code units
6165
 * (char16_t).
6166
 * @return true if and only if the string is valid UTF-16LE.
6167
 */
6168
simdutf_warn_unused bool validate_utf16le(const char16_t *buf,
6169
                                          size_t len) noexcept;
6170
  #if SIMDUTF_SPAN
6171
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused bool
6172
validate_utf16le(std::span<const char16_t> input) noexcept {
6173
    #if SIMDUTF_CPLUSPLUS23
6174
  if consteval {
6175
    return scalar::utf16::validate<endianness::LITTLE>(input.data(),
6176
                                                       input.size());
6177
  } else
6178
    #endif
6179
  {
6180
    return validate_utf16le(input.data(), input.size());
6181
  }
6182
}
6183
  #endif // SIMDUTF_SPAN
6184
#endif   // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
6185
6186
#if SIMDUTF_FEATURE_UTF16
6187
/**
6188
 * Validate the UTF-16BE string. This function may be best when you expect
6189
 * the input to be almost always valid. Otherwise, consider using
6190
 * validate_utf16be_with_errors.
6191
 *
6192
 * Overridden by each implementation.
6193
 *
6194
 * This function is not BOM-aware.
6195
 *
6196
 * @param buf the UTF-16BE string to validate.
6197
 * @param len the length of the string in number of 2-byte code units
6198
 * (char16_t).
6199
 * @return true if and only if the string is valid UTF-16BE.
6200
 */
6201
simdutf_warn_unused bool validate_utf16be(const char16_t *buf,
6202
                                          size_t len) noexcept;
6203
  #if SIMDUTF_SPAN
6204
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6205
validate_utf16be(std::span<const char16_t> input) noexcept {
6206
    #if SIMDUTF_CPLUSPLUS23
6207
  if consteval {
6208
    return scalar::utf16::validate<endianness::BIG>(input.data(), input.size());
6209
  } else
6210
    #endif
6211
  {
6212
    return validate_utf16be(input.data(), input.size());
6213
  }
6214
}
6215
  #endif // SIMDUTF_SPAN
6216
6217
/**
6218
 * Using native endianness; Validate the UTF-16 string and stop on error.
6219
 * It might be faster than validate_utf16 when an error is expected to occur
6220
 * early.
6221
 *
6222
 * Overridden by each implementation.
6223
 *
6224
 * This function is not BOM-aware.
6225
 *
6226
 * @param buf the UTF-16 string to validate.
6227
 * @param len the length of the string in number of 2-byte code units
6228
 * (char16_t).
6229
 * @return a result pair struct (of type simdutf::result containing the two
6230
 * fields error and count) with an error code and either position of the error
6231
 * (in the input in code units) if any, or the number of code units validated if
6232
 * successful.
6233
 */
6234
simdutf_warn_unused result validate_utf16_with_errors(const char16_t *buf,
6235
                                                      size_t len) noexcept;
6236
  #if SIMDUTF_SPAN
6237
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6238
validate_utf16_with_errors(std::span<const char16_t> input) noexcept {
6239
    #if SIMDUTF_CPLUSPLUS23
6240
  if consteval {
6241
    return scalar::utf16::validate_with_errors<endianness::NATIVE>(
6242
        input.data(), input.size());
6243
  } else
6244
    #endif
6245
  {
6246
    return validate_utf16_with_errors(input.data(), input.size());
6247
  }
6248
}
6249
  #endif // SIMDUTF_SPAN
6250
6251
/**
6252
 * Validate the UTF-16LE string and stop on error. It might be faster than
6253
 * validate_utf16le when an error is expected to occur early.
6254
 *
6255
 * Overridden by each implementation.
6256
 *
6257
 * This function is not BOM-aware.
6258
 *
6259
 * @param buf the UTF-16LE string to validate.
6260
 * @param len the length of the string in number of 2-byte code units
6261
 * (char16_t).
6262
 * @return a result pair struct (of type simdutf::result containing the two
6263
 * fields error and count) with an error code and either position of the error
6264
 * (in the input in code units) if any, or the number of code units validated if
6265
 * successful.
6266
 */
6267
simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf,
6268
                                                        size_t len) noexcept;
6269
  #if SIMDUTF_SPAN
6270
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6271
validate_utf16le_with_errors(std::span<const char16_t> input) noexcept {
6272
    #if SIMDUTF_CPLUSPLUS23
6273
  if consteval {
6274
    return scalar::utf16::validate_with_errors<endianness::LITTLE>(
6275
        input.data(), input.size());
6276
  } else
6277
    #endif
6278
  {
6279
    return validate_utf16le_with_errors(input.data(), input.size());
6280
  }
6281
}
6282
  #endif // SIMDUTF_SPAN
6283
6284
/**
6285
 * Validate the UTF-16BE string and stop on error. It might be faster than
6286
 * validate_utf16be when an error is expected to occur early.
6287
 *
6288
 * Overridden by each implementation.
6289
 *
6290
 * This function is not BOM-aware.
6291
 *
6292
 * @param buf the UTF-16BE string to validate.
6293
 * @param len the length of the string in number of 2-byte code units
6294
 * (char16_t).
6295
 * @return a result pair struct (of type simdutf::result containing the two
6296
 * fields error and count) with an error code and either position of the error
6297
 * (in the input in code units) if any, or the number of code units validated if
6298
 * successful.
6299
 */
6300
simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf,
6301
                                                        size_t len) noexcept;
6302
  #if SIMDUTF_SPAN
6303
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6304
validate_utf16be_with_errors(std::span<const char16_t> input) noexcept {
6305
    #if SIMDUTF_CPLUSPLUS23
6306
  if consteval {
6307
    return scalar::utf16::validate_with_errors<endianness::BIG>(input.data(),
6308
                                                                input.size());
6309
  } else
6310
    #endif
6311
  {
6312
    return validate_utf16be_with_errors(input.data(), input.size());
6313
  }
6314
}
6315
  #endif // SIMDUTF_SPAN
6316
6317
/**
6318
 * Fixes an ill-formed UTF-16LE string by replacing mismatched surrogates with
6319
 * the Unicode replacement character U+FFFD. If input and output points to
6320
 * different memory areas, the procedure copies string, and it's expected that
6321
 * output memory is at least as big as the input. It's also possible to set
6322
 * input equal output, that makes replacements an in-place operation.
6323
 *
6324
 * @param input the UTF-16LE string to correct.
6325
 * @param len the length of the string in number of 2-byte code units
6326
 * (char16_t).
6327
 * @param output the output buffer.
6328
 */
6329
void to_well_formed_utf16le(const char16_t *input, size_t len,
6330
                            char16_t *output) noexcept;
6331
  #if SIMDUTF_SPAN
6332
simdutf_really_inline simdutf_constexpr23 void
6333
to_well_formed_utf16le(std::span<const char16_t> input,
6334
                       std::span<char16_t> output) noexcept {
6335
    #if SIMDUTF_CPLUSPLUS23
6336
  if consteval {
6337
    scalar::utf16::to_well_formed_utf16<endianness::LITTLE>(
6338
        input.data(), input.size(), output.data());
6339
  } else
6340
    #endif
6341
  {
6342
    to_well_formed_utf16le(input.data(), input.size(), output.data());
6343
  }
6344
}
6345
  #endif // SIMDUTF_SPAN
6346
6347
/**
6348
 * Fixes an ill-formed UTF-16BE string by replacing mismatched surrogates with
6349
 * the Unicode replacement character U+FFFD. If input and output points to
6350
 * different memory areas, the procedure copies string, and it's expected that
6351
 * output memory is at least as big as the input. It's also possible to set
6352
 * input equal output, that makes replacements an in-place operation.
6353
 *
6354
 * @param input the UTF-16BE string to correct.
6355
 * @param len the length of the string in number of 2-byte code units
6356
 * (char16_t).
6357
 * @param output the output buffer.
6358
 */
6359
void to_well_formed_utf16be(const char16_t *input, size_t len,
6360
                            char16_t *output) noexcept;
6361
  #if SIMDUTF_SPAN
6362
simdutf_really_inline simdutf_constexpr23 void
6363
to_well_formed_utf16be(std::span<const char16_t> input,
6364
                       std::span<char16_t> output) noexcept {
6365
    #if SIMDUTF_CPLUSPLUS23
6366
  if consteval {
6367
    scalar::utf16::to_well_formed_utf16<endianness::BIG>(
6368
        input.data(), input.size(), output.data());
6369
  } else
6370
    #endif
6371
  {
6372
    to_well_formed_utf16be(input.data(), input.size(), output.data());
6373
  }
6374
}
6375
  #endif // SIMDUTF_SPAN
6376
6377
/**
6378
 * Fixes an ill-formed UTF-16 string by replacing mismatched surrogates with the
6379
 * Unicode replacement character U+FFFD. If input and output points to different
6380
 * memory areas, the procedure copies string, and it's expected that output
6381
 * memory is at least as big as the input. It's also possible to set input equal
6382
 * output, that makes replacements an in-place operation.
6383
 *
6384
 * @param input the UTF-16 string to correct.
6385
 * @param len the length of the string in number of 2-byte code units
6386
 * (char16_t).
6387
 * @param output the output buffer.
6388
 */
6389
void to_well_formed_utf16(const char16_t *input, size_t len,
6390
                          char16_t *output) noexcept;
6391
  #if SIMDUTF_SPAN
6392
simdutf_really_inline simdutf_constexpr23 void
6393
to_well_formed_utf16(std::span<const char16_t> input,
6394
                     std::span<char16_t> output) noexcept {
6395
    #if SIMDUTF_CPLUSPLUS23
6396
  if consteval {
6397
    scalar::utf16::to_well_formed_utf16<endianness::NATIVE>(
6398
        input.data(), input.size(), output.data());
6399
  } else
6400
    #endif
6401
  {
6402
    to_well_formed_utf16(input.data(), input.size(), output.data());
6403
  }
6404
}
6405
  #endif // SIMDUTF_SPAN
6406
6407
#endif // SIMDUTF_FEATURE_UTF16
6408
6409
#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
6410
/**
6411
 * Validate the UTF-32 string. This function may be best when you expect
6412
 * the input to be almost always valid. Otherwise, consider using
6413
 * validate_utf32_with_errors.
6414
 *
6415
 * Overridden by each implementation.
6416
 *
6417
 * This function is not BOM-aware.
6418
 *
6419
 * @param buf the UTF-32 string to validate.
6420
 * @param len the length of the string in number of 4-byte code units
6421
 * (char32_t).
6422
 * @return true if and only if the string is valid UTF-32.
6423
 */
6424
simdutf_warn_unused bool validate_utf32(const char32_t *buf,
6425
                                        size_t len) noexcept;
6426
  #if SIMDUTF_SPAN
6427
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 bool
6428
validate_utf32(std::span<const char32_t> input) noexcept {
6429
    #if SIMDUTF_CPLUSPLUS23
6430
  if consteval {
6431
    return scalar::utf32::validate(
6432
        detail::constexpr_cast_ptr<std::uint32_t>(input.data()), input.size());
6433
  } else
6434
    #endif
6435
  {
6436
    return validate_utf32(input.data(), input.size());
6437
  }
6438
}
6439
  #endif // SIMDUTF_SPAN
6440
#endif   // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
6441
6442
#if SIMDUTF_FEATURE_UTF32
6443
/**
6444
 * Validate the UTF-32 string and stop on error. It might be faster than
6445
 * validate_utf32 when an error is expected to occur early.
6446
 *
6447
 * Overridden by each implementation.
6448
 *
6449
 * This function is not BOM-aware.
6450
 *
6451
 * @param buf the UTF-32 string to validate.
6452
 * @param len the length of the string in number of 4-byte code units
6453
 * (char32_t).
6454
 * @return a result pair struct (of type simdutf::result containing the two
6455
 * fields error and count) with an error code and either position of the error
6456
 * (in the input in code units) if any, or the number of code units validated if
6457
 * successful.
6458
 */
6459
simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf,
6460
                                                      size_t len) noexcept;
6461
  #if SIMDUTF_SPAN
6462
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6463
validate_utf32_with_errors(std::span<const char32_t> input) noexcept {
6464
    #if SIMDUTF_CPLUSPLUS23
6465
  if consteval {
6466
    return scalar::utf32::validate_with_errors(
6467
        detail::constexpr_cast_ptr<std::uint32_t>(input.data()), input.size());
6468
  } else
6469
    #endif
6470
  {
6471
    return validate_utf32_with_errors(input.data(), input.size());
6472
  }
6473
}
6474
  #endif // SIMDUTF_SPAN
6475
#endif   // SIMDUTF_FEATURE_UTF32
6476
6477
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6478
/**
6479
 * Convert Latin1 string into UTF-8 string.
6480
 *
6481
 * This function is suitable to work with inputs from untrusted sources.
6482
 *
6483
 * @param input         the Latin1 string to convert
6484
 * @param length        the length of the string in bytes
6485
 * @param utf8_output   the pointer to buffer that can hold conversion result
6486
 * @return the number of written char; 0 if conversion is not possible
6487
 */
6488
simdutf_warn_unused size_t convert_latin1_to_utf8(const char *input,
6489
                                                  size_t length,
6490
                                                  char *utf8_output) noexcept;
6491
  #if SIMDUTF_SPAN
6492
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6493
convert_latin1_to_utf8(
6494
    const detail::input_span_of_byte_like auto &latin1_input,
6495
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
6496
    #if SIMDUTF_CPLUSPLUS23
6497
  if consteval {
6498
    return scalar::latin1_to_utf8::convert(
6499
        detail::constexpr_cast_ptr<char>(latin1_input.data()),
6500
        latin1_input.size(),
6501
        detail::constexpr_cast_writeptr<char>(utf8_output.data()));
6502
  } else
6503
    #endif
6504
  {
6505
    return convert_latin1_to_utf8(
6506
        reinterpret_cast<const char *>(latin1_input.data()),
6507
        latin1_input.size(), reinterpret_cast<char *>(utf8_output.data()));
6508
  }
6509
}
6510
  #endif // SIMDUTF_SPAN
6511
6512
/**
6513
 * Convert Latin1 string into UTF-8 string with output limit.
6514
 *
6515
 * This function is suitable to work with inputs from untrusted sources.
6516
 *
6517
 * We write as many characters as possible.
6518
 *
6519
 * Using convert_latin1_to_utf8_safe instead of convert_latin1_to_utf8 comes
6520
 * with a significant penalty in some cases, being up to four times slower,
6521
 * especially on short inputs. If you have allocated the output buffer so that
6522
 * it contains utf8_length_from_latin1(input, length) bytes, then prefer
6523
 * convert_latin1_to_utf8.
6524
 *
6525
 * @param input         the Latin1 string to convert
6526
 * @param length        the length of the string in bytes
6527
 * @param utf8_output   the pointer to buffer that can hold conversion result
6528
 * @param utf8_len      the maximum output length
6529
 * @return the number of written char; 0 if conversion is not possible
6530
 */
6531
simdutf_warn_unused size_t
6532
convert_latin1_to_utf8_safe(const char *input, size_t length, char *utf8_output,
6533
                            size_t utf8_len) noexcept;
6534
  #if SIMDUTF_SPAN
6535
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6536
convert_latin1_to_utf8_safe(
6537
    const detail::input_span_of_byte_like auto &input,
6538
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
6539
      // implementation note: outputspan is a forwarding ref to avoid copying
6540
      // and allow both lvalues and rvalues. std::span can be copied without
6541
      // problems, but std::vector should not, and this function should accept
6542
      // both. it will allow using an owning rvalue ref (example: passing a
6543
      // temporary std::string) as output, but the user will quickly find out
6544
      // that he has no way of getting the data out of the object in that case.
6545
    #if SIMDUTF_CPLUSPLUS23
6546
  if consteval {
6547
    return scalar::latin1_to_utf8::convert_safe_constexpr(
6548
        input.data(), input.size(), utf8_output.data(), utf8_output.size());
6549
  } else
6550
    #endif
6551
  {
6552
    return convert_latin1_to_utf8_safe(
6553
        reinterpret_cast<const char *>(input.data()), input.size(),
6554
        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
6555
  }
6556
}
6557
  #endif // SIMDUTF_SPAN
6558
6559
/**
6560
 * Convert a Latin1 string into a size-limited UTF-8 buffer and report how much
6561
 * input was consumed and output was written.
6562
 *
6563
 * We write as many complete characters as possible. The returned error is
6564
 * SUCCESS if all input was consumed, or OUTPUT_BUFFER_TOO_SMALL otherwise.
6565
 *
6566
 * @param input         the Latin1 string to convert
6567
 * @param length        the length of the string in bytes
6568
 * @param utf8_output   the pointer to the output buffer
6569
 * @param utf8_len      the maximum output length
6570
 * @return a full_result with error, input_count and output_count
6571
 */
6572
simdutf_warn_unused full_result convert_latin1_to_utf8_safe_with_details(
6573
    const char *input, size_t length, char *utf8_output,
6574
    size_t utf8_len) noexcept;
6575
  #if SIMDUTF_SPAN
6576
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result
6577
convert_latin1_to_utf8_safe_with_details(
6578
    const detail::input_span_of_byte_like auto &input,
6579
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
6580
    #if SIMDUTF_CPLUSPLUS23
6581
  if consteval {
6582
    return scalar::latin1_to_utf8::convert_safe_with_details_constexpr(
6583
        input.data(), input.size(), utf8_output.data(), utf8_output.size());
6584
  } else
6585
    #endif
6586
  {
6587
    return convert_latin1_to_utf8_safe_with_details(
6588
        reinterpret_cast<const char *>(input.data()), input.size(),
6589
        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
6590
  }
6591
}
6592
  #endif // SIMDUTF_SPAN
6593
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6594
6595
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
6596
/**
6597
 * Convert possibly Latin1 string into UTF-16LE string.
6598
 *
6599
 * This function is suitable to work with inputs from untrusted sources.
6600
 *
6601
 * @param input         the Latin1 string to convert
6602
 * @param length        the length of the string in bytes
6603
 * @param utf16_output  the pointer to buffer that can hold conversion result
6604
 * @return the number of written char16_t; 0 if conversion is not possible
6605
 */
6606
simdutf_warn_unused size_t convert_latin1_to_utf16le(
6607
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6608
  #if SIMDUTF_SPAN
6609
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6610
convert_latin1_to_utf16le(
6611
    const detail::input_span_of_byte_like auto &latin1_input,
6612
    std::span<char16_t> utf16_output) noexcept {
6613
    #if SIMDUTF_CPLUSPLUS23
6614
  if consteval {
6615
    return scalar::latin1_to_utf16::convert<endianness::LITTLE>(
6616
        latin1_input.data(), latin1_input.size(), utf16_output.data());
6617
  } else
6618
    #endif
6619
  {
6620
    return convert_latin1_to_utf16le(
6621
        reinterpret_cast<const char *>(latin1_input.data()),
6622
        latin1_input.size(), utf16_output.data());
6623
  }
6624
}
6625
  #endif // SIMDUTF_SPAN
6626
6627
/**
6628
 * Convert Latin1 string into UTF-16BE string.
6629
 *
6630
 * This function is suitable to work with inputs from untrusted sources.
6631
 *
6632
 * @param input         the Latin1 string to convert
6633
 * @param length        the length of the string in bytes
6634
 * @param utf16_output  the pointer to buffer that can hold conversion result
6635
 * @return the number of written char16_t; 0 if conversion is not possible
6636
 */
6637
simdutf_warn_unused size_t convert_latin1_to_utf16be(
6638
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6639
  #if SIMDUTF_SPAN
6640
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6641
convert_latin1_to_utf16be(const detail::input_span_of_byte_like auto &input,
6642
                          std::span<char16_t> output) noexcept {
6643
    #if SIMDUTF_CPLUSPLUS23
6644
  if consteval {
6645
    return scalar::latin1_to_utf16::convert<endianness::BIG>(
6646
        input.data(), input.size(), output.data());
6647
  } else
6648
    #endif
6649
  {
6650
    return convert_latin1_to_utf16be(
6651
        reinterpret_cast<const char *>(input.data()), input.size(),
6652
        output.data());
6653
  }
6654
}
6655
  #endif // SIMDUTF_SPAN
6656
/**
6657
 * Compute the number of bytes that this UTF-16 string would require in Latin1
6658
 * format.
6659
 *
6660
 * @param length        the length of the string in Latin1 code units (char)
6661
 * @return the length of the string in Latin1 code units (char) required to
6662
 * encode the UTF-16 string as Latin1
6663
 */
6664
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6665
latin1_length_from_utf16(size_t length) noexcept {
6666
  return length;
6667
}
6668
6669
/**
6670
 * Compute the number of code units that this Latin1 string would require in
6671
 * UTF-16 format.
6672
 *
6673
 * @param length        the length of the string in Latin1 code units (char)
6674
 * @return the length of the string in 2-byte code units (char16_t) required to
6675
 * encode the Latin1 string as UTF-16
6676
 */
6677
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6678
utf16_length_from_latin1(size_t length) noexcept {
6679
  return length;
6680
}
6681
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
6682
6683
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
6684
/**
6685
 * Convert Latin1 string into UTF-32 string.
6686
 *
6687
 * This function is suitable to work with inputs from untrusted sources.
6688
 *
6689
 * @param input         the Latin1 string to convert
6690
 * @param length        the length of the string in bytes
6691
 * @param utf32_buffer  the pointer to buffer that can hold conversion result
6692
 * @return the number of written char32_t; 0 if conversion is not possible
6693
 */
6694
simdutf_warn_unused size_t convert_latin1_to_utf32(
6695
    const char *input, size_t length, char32_t *utf32_buffer) noexcept;
6696
  #if SIMDUTF_SPAN
6697
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6698
convert_latin1_to_utf32(
6699
    const detail::input_span_of_byte_like auto &latin1_input,
6700
    std::span<char32_t> utf32_output) noexcept {
6701
    #if SIMDUTF_CPLUSPLUS23
6702
  if consteval {
6703
    return scalar::latin1_to_utf32::convert(
6704
        latin1_input.data(), latin1_input.size(), utf32_output.data());
6705
  } else
6706
    #endif
6707
  {
6708
    return convert_latin1_to_utf32(
6709
        reinterpret_cast<const char *>(latin1_input.data()),
6710
        latin1_input.size(), utf32_output.data());
6711
  }
6712
}
6713
  #endif // SIMDUTF_SPAN
6714
#endif   // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
6715
6716
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6717
/**
6718
 * Convert possibly broken UTF-8 string into latin1 string.
6719
 *
6720
 * During the conversion also validation of the input string is done.
6721
 * This function is suitable to work with inputs from untrusted sources.
6722
 *
6723
 * @param input         the UTF-8 string to convert
6724
 * @param length        the length of the string in bytes
6725
 * @param latin1_output  the pointer to buffer that can hold conversion result
6726
 * @return the number of written char; 0 if the input was not valid UTF-8 string
6727
 * or if it cannot be represented as Latin1
6728
 */
6729
simdutf_warn_unused size_t convert_utf8_to_latin1(const char *input,
6730
                                                  size_t length,
6731
                                                  char *latin1_output) noexcept;
6732
  #if SIMDUTF_SPAN
6733
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6734
convert_utf8_to_latin1(
6735
    const detail::input_span_of_byte_like auto &input,
6736
    detail::output_span_of_byte_like auto &&output) noexcept {
6737
    #if SIMDUTF_CPLUSPLUS23
6738
  if consteval {
6739
    return scalar::utf8_to_latin1::convert(input.data(), input.size(),
6740
                                           output.data());
6741
  } else
6742
    #endif
6743
  {
6744
    return convert_utf8_to_latin1(reinterpret_cast<const char *>(input.data()),
6745
                                  input.size(),
6746
                                  reinterpret_cast<char *>(output.data()));
6747
  }
6748
}
6749
  #endif // SIMDUTF_SPAN
6750
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6751
6752
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
6753
/**
6754
 * Using native endianness, convert possibly broken UTF-8 string into a UTF-16
6755
 * string.
6756
 *
6757
 * During the conversion also validation of the input string is done.
6758
 * This function is suitable to work with inputs from untrusted sources.
6759
 *
6760
 * @param input         the UTF-8 string to convert
6761
 * @param length        the length of the string in bytes
6762
 * @param utf16_output  the pointer to buffer that can hold conversion result
6763
 * @return the number of written char16_t; 0 if the input was not valid UTF-8
6764
 * string
6765
 */
6766
simdutf_warn_unused size_t convert_utf8_to_utf16(
6767
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6768
  #if SIMDUTF_SPAN
6769
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6770
convert_utf8_to_utf16(const detail::input_span_of_byte_like auto &input,
6771
                      std::span<char16_t> output) noexcept {
6772
    #if SIMDUTF_CPLUSPLUS23
6773
  if consteval {
6774
    return scalar::utf8_to_utf16::convert<endianness::NATIVE>(
6775
        input.data(), input.size(), output.data());
6776
  } else
6777
    #endif
6778
  {
6779
    return convert_utf8_to_utf16(reinterpret_cast<const char *>(input.data()),
6780
                                 input.size(), output.data());
6781
  }
6782
}
6783
  #endif // SIMDUTF_SPAN
6784
6785
/**
6786
 * Compute the number of bytes that this UTF-16LE string would require in UTF-8
6787
 * format even when the UTF-16LE content contains mismatched surrogates
6788
 * that have to be replaced by the replacement character (0xFFFD).
6789
 *
6790
 * @param input         the UTF-16LE string to convert
6791
 * @param length        the length of the string in 2-byte code units (char16_t)
6792
 * @return a result pair struct (of type simdutf::result containing the two
6793
 * fields error and count) where the count is the number of bytes required to
6794
 * encode the UTF-16LE string as UTF-8, and the error code is either SUCCESS or
6795
 * SURROGATE. The count is correct regardless of the error field.
6796
 * When SURROGATE is returned, it does not indicate an error in the case of this
6797
 * function: it indicates that at least one surrogate has been encountered: the
6798
 * surrogates may be matched or not (thus this function does not validate). If
6799
 * the returned error code is SUCCESS, then the input contains no surrogate, is
6800
 * in the Basic Multilingual Plane, and is necessarily valid.
6801
 */
6802
simdutf_warn_unused result utf8_length_from_utf16le_with_replacement(
6803
    const char16_t *input, size_t length) noexcept;
6804
  #if SIMDUTF_SPAN
6805
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused result
6806
utf8_length_from_utf16le_with_replacement(
6807
    std::span<const char16_t> valid_utf16_input) noexcept {
6808
    #if SIMDUTF_CPLUSPLUS23
6809
  if consteval {
6810
    return scalar::utf16::utf8_length_from_utf16_with_replacement<
6811
        endianness::LITTLE>(valid_utf16_input.data(), valid_utf16_input.size());
6812
  } else
6813
    #endif
6814
  {
6815
    return utf8_length_from_utf16le_with_replacement(valid_utf16_input.data(),
6816
                                                     valid_utf16_input.size());
6817
  }
6818
}
6819
  #endif // SIMDUTF_SPAN
6820
6821
/**
6822
 * Compute the number of bytes that this UTF-16BE string would require in UTF-8
6823
 * format even when the UTF-16BE content contains mismatched surrogates
6824
 * that have to be replaced by the replacement character (0xFFFD).
6825
 *
6826
 * @param input         the UTF-16BE string to convert
6827
 * @param length        the length of the string in 2-byte code units (char16_t)
6828
 * @return a result pair struct (of type simdutf::result containing the two
6829
 * fields error and count) where the count is the number of bytes required to
6830
 * encode the UTF-16BE string as UTF-8, and the error code is either SUCCESS or
6831
 * SURROGATE. The count is correct regardless of the error field.
6832
 * When SURROGATE is returned, it does not indicate an error in the case of this
6833
 * function: it indicates that at least one surrogate has been encountered: the
6834
 * surrogates may be matched or not (thus this function does not validate). If
6835
 * the returned error code is SUCCESS, then the input contains no surrogate, is
6836
 * in the Basic Multilingual Plane, and is necessarily valid.
6837
 */
6838
simdutf_warn_unused result utf8_length_from_utf16be_with_replacement(
6839
    const char16_t *input, size_t length) noexcept;
6840
  #if SIMDUTF_SPAN
6841
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6842
utf8_length_from_utf16be_with_replacement(
6843
    std::span<const char16_t> valid_utf16_input) noexcept {
6844
    #if SIMDUTF_CPLUSPLUS23
6845
  if consteval {
6846
    return scalar::utf16::utf8_length_from_utf16_with_replacement<
6847
        endianness::BIG>(valid_utf16_input.data(), valid_utf16_input.size());
6848
  } else
6849
    #endif
6850
  {
6851
    return utf8_length_from_utf16be_with_replacement(valid_utf16_input.data(),
6852
                                                     valid_utf16_input.size());
6853
  }
6854
}
6855
  #endif // SIMDUTF_SPAN
6856
6857
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
6858
6859
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
6860
/**
6861
 * Using native endianness, convert a Latin1 string into a UTF-16 string.
6862
 *
6863
 * @param input         the Latin1 string to convert
6864
 * @param length        the length of the string in bytes
6865
 * @param utf16_output  the pointer to buffer that can hold conversion result
6866
 * @return the number of written char16_t.
6867
 */
6868
simdutf_warn_unused size_t convert_latin1_to_utf16(
6869
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6870
  #if SIMDUTF_SPAN
6871
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6872
convert_latin1_to_utf16(const detail::input_span_of_byte_like auto &input,
6873
                        std::span<char16_t> output) noexcept {
6874
    #if SIMDUTF_CPLUSPLUS23
6875
  if consteval {
6876
    return scalar::latin1_to_utf16::convert<endianness::NATIVE>(
6877
        input.data(), input.size(), output.data());
6878
  } else
6879
    #endif
6880
  {
6881
    return convert_latin1_to_utf16(reinterpret_cast<const char *>(input.data()),
6882
                                   input.size(), output.data());
6883
  }
6884
}
6885
  #endif // SIMDUTF_SPAN
6886
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
6887
6888
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
6889
/**
6890
 * Convert possibly broken UTF-8 string into UTF-16LE string.
6891
 *
6892
 * During the conversion also validation of the input string is done.
6893
 * This function is suitable to work with inputs from untrusted sources.
6894
 *
6895
 * @param input         the UTF-8 string to convert
6896
 * @param length        the length of the string in bytes
6897
 * @param utf16_output  the pointer to buffer that can hold conversion result
6898
 * @return the number of written char16_t; 0 if the input was not valid UTF-8
6899
 * string
6900
 */
6901
simdutf_warn_unused size_t convert_utf8_to_utf16le(
6902
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6903
  #if SIMDUTF_SPAN
6904
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6905
convert_utf8_to_utf16le(const detail::input_span_of_byte_like auto &utf8_input,
6906
                        std::span<char16_t> utf16_output) noexcept {
6907
    #if SIMDUTF_CPLUSPLUS23
6908
  if consteval {
6909
    return scalar::utf8_to_utf16::convert<endianness::LITTLE>(
6910
        utf8_input.data(), utf8_input.size(), utf16_output.data());
6911
  } else
6912
    #endif
6913
  {
6914
    return convert_utf8_to_utf16le(
6915
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
6916
        utf16_output.data());
6917
  }
6918
}
6919
  #endif // SIMDUTF_SPAN
6920
6921
/**
6922
 * Convert possibly broken UTF-8 string into UTF-16BE string.
6923
 *
6924
 * During the conversion also validation of the input string is done.
6925
 * This function is suitable to work with inputs from untrusted sources.
6926
 *
6927
 * @param input         the UTF-8 string to convert
6928
 * @param length        the length of the string in bytes
6929
 * @param utf16_output  the pointer to buffer that can hold conversion result
6930
 * @return the number of written char16_t; 0 if the input was not valid UTF-8
6931
 * string
6932
 */
6933
simdutf_warn_unused size_t convert_utf8_to_utf16be(
6934
    const char *input, size_t length, char16_t *utf16_output) noexcept;
6935
  #if SIMDUTF_SPAN
6936
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
6937
convert_utf8_to_utf16be(const detail::input_span_of_byte_like auto &utf8_input,
6938
                        std::span<char16_t> utf16_output) noexcept {
6939
6940
    #if SIMDUTF_CPLUSPLUS23
6941
  if consteval {
6942
    return scalar::utf8_to_utf16::convert<endianness::BIG>(
6943
        utf8_input.data(), utf8_input.size(), utf16_output.data());
6944
  } else
6945
    #endif
6946
  {
6947
    return convert_utf8_to_utf16be(
6948
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
6949
        utf16_output.data());
6950
  }
6951
}
6952
  #endif // SIMDUTF_SPAN
6953
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
6954
6955
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6956
/**
6957
 * Convert possibly broken UTF-8 string into latin1 string with errors.
6958
 * If the string cannot be represented as Latin1, an error
6959
 * code is returned.
6960
 *
6961
 * During the conversion also validation of the input string is done.
6962
 * This function is suitable to work with inputs from untrusted sources.
6963
 *
6964
 * @param input         the UTF-8 string to convert
6965
 * @param length        the length of the string in bytes
6966
 * @param latin1_output  the pointer to buffer that can hold conversion result
6967
 * @return a result pair struct (of type simdutf::result containing the two
6968
 * fields error and count) with an error code and either position of the error
6969
 * (in the input in code units) if any, or the number of code units validated if
6970
 * successful.
6971
 */
6972
simdutf_warn_unused result convert_utf8_to_latin1_with_errors(
6973
    const char *input, size_t length, char *latin1_output) noexcept;
6974
  #if SIMDUTF_SPAN
6975
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
6976
convert_utf8_to_latin1_with_errors(
6977
    const detail::input_span_of_byte_like auto &utf8_input,
6978
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
6979
    #if SIMDUTF_CPLUSPLUS23
6980
  if consteval {
6981
    return scalar::utf8_to_latin1::convert_with_errors(
6982
        utf8_input.data(), utf8_input.size(), latin1_output.data());
6983
  } else
6984
    #endif
6985
  {
6986
    return convert_utf8_to_latin1_with_errors(
6987
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
6988
        reinterpret_cast<char *>(latin1_output.data()));
6989
  }
6990
}
6991
  #endif // SIMDUTF_SPAN
6992
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
6993
6994
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
6995
/**
6996
 * Using native endianness, convert possibly broken UTF-8 string into UTF-16
6997
 * string and stop on error.
6998
 *
6999
 * During the conversion also validation of the input string is done.
7000
 * This function is suitable to work with inputs from untrusted sources.
7001
 *
7002
 * @param input         the UTF-8 string to convert
7003
 * @param length        the length of the string in bytes
7004
 * @param utf16_output  the pointer to buffer that can hold conversion result
7005
 * @return a result pair struct (of type simdutf::result containing the two
7006
 * fields error and count) with an error code and either position of the error
7007
 * (in the input in code units) if any, or the number of char16_t written if
7008
 * successful.
7009
 */
7010
simdutf_warn_unused result convert_utf8_to_utf16_with_errors(
7011
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7012
  #if SIMDUTF_SPAN
7013
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
7014
convert_utf8_to_utf16_with_errors(
7015
    const detail::input_span_of_byte_like auto &utf8_input,
7016
    std::span<char16_t> utf16_output) noexcept {
7017
    #if SIMDUTF_CPLUSPLUS23
7018
  if consteval {
7019
    return scalar::utf8_to_utf16::convert_with_errors<endianness::NATIVE>(
7020
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7021
  } else
7022
    #endif
7023
  {
7024
    return convert_utf8_to_utf16_with_errors(
7025
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7026
        utf16_output.data());
7027
  }
7028
}
7029
  #endif // SIMDUTF_SPAN
7030
7031
/**
7032
 * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error.
7033
 *
7034
 * During the conversion also validation of the input string is done.
7035
 * This function is suitable to work with inputs from untrusted sources.
7036
 *
7037
 * @param input         the UTF-8 string to convert
7038
 * @param length        the length of the string in bytes
7039
 * @param utf16_output  the pointer to buffer that can hold conversion result
7040
 * @return a result pair struct (of type simdutf::result containing the two
7041
 * fields error and count) with an error code and either position of the error
7042
 * (in the input in code units) if any, or the number of char16_t written if
7043
 * successful.
7044
 */
7045
simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(
7046
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7047
  #if SIMDUTF_SPAN
7048
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
7049
convert_utf8_to_utf16le_with_errors(
7050
    const detail::input_span_of_byte_like auto &utf8_input,
7051
    std::span<char16_t> utf16_output) noexcept {
7052
    #if SIMDUTF_CPLUSPLUS23
7053
  if consteval {
7054
    return scalar::utf8_to_utf16::convert_with_errors<endianness::LITTLE>(
7055
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7056
  } else
7057
    #endif
7058
  {
7059
    return convert_utf8_to_utf16le_with_errors(
7060
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7061
        utf16_output.data());
7062
  }
7063
}
7064
  #endif // SIMDUTF_SPAN
7065
7066
/**
7067
 * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error.
7068
 *
7069
 * During the conversion also validation of the input string is done.
7070
 * This function is suitable to work with inputs from untrusted sources.
7071
 *
7072
 * @param input         the UTF-8 string to convert
7073
 * @param length        the length of the string in bytes
7074
 * @param utf16_output  the pointer to buffer that can hold conversion result
7075
 * @return a result pair struct (of type simdutf::result containing the two
7076
 * fields error and count) with an error code and either position of the error
7077
 * (in the input in code units) if any, or the number of char16_t written if
7078
 * successful.
7079
 */
7080
simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(
7081
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7082
  #if SIMDUTF_SPAN
7083
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
7084
convert_utf8_to_utf16be_with_errors(
7085
    const detail::input_span_of_byte_like auto &utf8_input,
7086
    std::span<char16_t> utf16_output) noexcept {
7087
    #if SIMDUTF_CPLUSPLUS23
7088
  if consteval {
7089
    return scalar::utf8_to_utf16::convert_with_errors<endianness::BIG>(
7090
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7091
  } else
7092
    #endif
7093
  {
7094
    return convert_utf8_to_utf16be_with_errors(
7095
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7096
        utf16_output.data());
7097
  }
7098
}
7099
  #endif // SIMDUTF_SPAN
7100
7101
/**
7102
 * Convert a possibly ill-formed UTF-8 string into a UTF-16 string (native
7103
 * endianness), replacing each ill-formed subsequence with U+FFFD.
7104
 *
7105
 * Substitution follows Unicode maximal subparts (the WHATWG rule): each
7106
 * maximal subpart of an ill-formed sequence becomes one U+FFFD code unit.
7107
 * The function always succeeds. The output buffer must hold
7108
 * utf16_length_from_utf8_with_replacement(...).count char16_t. A buffer of
7109
 * `length` char16_t is always large enough. Pass that result to the overload
7110
 * that takes it, so the recorded error locations are not discovered again.
7111
 *
7112
 * This function is not BOM-aware.
7113
 *
7114
 * @param input         the UTF-8 string to convert
7115
 * @param length        the length of the string in bytes
7116
 * @param utf16_output  the pointer to buffer that can hold the conversion
7117
 * result
7118
 * @return the number of written char16_t
7119
 */
7120
simdutf_warn_unused size_t convert_utf8_to_utf16_with_replacement(
7121
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7122
  #if SIMDUTF_SPAN
7123
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7124
convert_utf8_to_utf16_with_replacement(
7125
    const detail::input_span_of_byte_like auto &utf8_input,
7126
    std::span<char16_t> utf16_output) noexcept {
7127
    #if SIMDUTF_CPLUSPLUS23
7128
  if consteval {
7129
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::NATIVE>(
7130
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7131
  } else
7132
    #endif
7133
  {
7134
    return convert_utf8_to_utf16_with_replacement(
7135
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7136
        utf16_output.data());
7137
  }
7138
}
7139
  #endif // SIMDUTF_SPAN
7140
7141
/**
7142
 * Convert a possibly ill-formed UTF-8 string into a UTF-16 string (native
7143
 * endianness), replacing each ill-formed subsequence with U+FFFD, using error
7144
 * locations already computed for this same input.
7145
 *
7146
 * locations must be the value returned by
7147
 * utf16_length_from_utf8_with_replacement for these bytes. When
7148
 * locations.more_errors is false, every ill-formed subsequence is in
7149
 * locations.error_offset and the gaps are valid UTF-8. When it is true, the
7150
 * input has further ill-formed subsequences, and the tail past the stored
7151
 * locations is scanned during conversion. The function always succeeds.
7152
 *
7153
 * @param input         the UTF-8 string to convert
7154
 * @param length        the length of the string in bytes
7155
 * @param utf16_output  the pointer to buffer that can hold the conversion
7156
 * result
7157
 * @param locations     result of utf16_length_from_utf8_with_replacement
7158
 * @return the number of written char16_t, equal to locations.count when
7159
 * locations describes this input
7160
 */
7161
simdutf_warn_unused size_t convert_utf8_to_utf16_with_replacement(
7162
    const char *input, size_t length, char16_t *utf16_output,
7163
    const utf8_to_utf16_result &locations) noexcept;
7164
  #if SIMDUTF_SPAN
7165
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7166
convert_utf8_to_utf16_with_replacement(
7167
    const detail::input_span_of_byte_like auto &utf8_input,
7168
    std::span<char16_t> utf16_output,
7169
    const utf8_to_utf16_result &locations) noexcept {
7170
    #if SIMDUTF_CPLUSPLUS23
7171
  if consteval {
7172
    (void)locations;
7173
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::NATIVE>(
7174
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7175
  } else
7176
    #endif
7177
  {
7178
    return convert_utf8_to_utf16_with_replacement(
7179
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7180
        utf16_output.data(), locations);
7181
  }
7182
}
7183
  #endif // SIMDUTF_SPAN
7184
7185
/**
7186
 * Convert a possibly ill-formed UTF-8 string into a UTF-16LE string, replacing
7187
 * each ill-formed subsequence with U+FFFD.
7188
 *
7189
 * Substitution follows Unicode maximal subparts (the WHATWG rule): each
7190
 * maximal subpart of an ill-formed sequence becomes one U+FFFD code unit.
7191
 * The function always succeeds. The output buffer must hold
7192
 * utf16_length_from_utf8_with_replacement(...).count char16_t. A buffer of
7193
 * `length` char16_t is always large enough. Pass that result to the overload
7194
 * that takes it, so the recorded error locations are not discovered again.
7195
 *
7196
 * This function is not BOM-aware.
7197
 *
7198
 * @param input         the UTF-8 string to convert
7199
 * @param length        the length of the string in bytes
7200
 * @param utf16_output  the pointer to buffer that can hold the conversion
7201
 * result
7202
 * @return the number of written char16_t
7203
 */
7204
simdutf_warn_unused size_t convert_utf8_to_utf16le_with_replacement(
7205
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7206
  #if SIMDUTF_SPAN
7207
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7208
convert_utf8_to_utf16le_with_replacement(
7209
    const detail::input_span_of_byte_like auto &utf8_input,
7210
    std::span<char16_t> utf16_output) noexcept {
7211
    #if SIMDUTF_CPLUSPLUS23
7212
  if consteval {
7213
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::LITTLE>(
7214
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7215
  } else
7216
    #endif
7217
  {
7218
    return convert_utf8_to_utf16le_with_replacement(
7219
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7220
        utf16_output.data());
7221
  }
7222
}
7223
  #endif // SIMDUTF_SPAN
7224
7225
/**
7226
 * Convert a possibly ill-formed UTF-8 string into a UTF-16LE string, using
7227
 * error locations already computed for this same input. See
7228
 * convert_utf8_to_utf16_with_replacement.
7229
 *
7230
 * @param input         the UTF-8 string to convert
7231
 * @param length        the length of the string in bytes
7232
 * @param utf16_output  the pointer to buffer that can hold the conversion
7233
 * result
7234
 * @param locations     result of utf16_length_from_utf8_with_replacement
7235
 * @return the number of written char16_t
7236
 */
7237
simdutf_warn_unused size_t convert_utf8_to_utf16le_with_replacement(
7238
    const char *input, size_t length, char16_t *utf16_output,
7239
    const utf8_to_utf16_result &locations) noexcept;
7240
  #if SIMDUTF_SPAN
7241
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7242
convert_utf8_to_utf16le_with_replacement(
7243
    const detail::input_span_of_byte_like auto &utf8_input,
7244
    std::span<char16_t> utf16_output,
7245
    const utf8_to_utf16_result &locations) noexcept {
7246
    #if SIMDUTF_CPLUSPLUS23
7247
  if consteval {
7248
    (void)locations;
7249
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::LITTLE>(
7250
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7251
  } else
7252
    #endif
7253
  {
7254
    return convert_utf8_to_utf16le_with_replacement(
7255
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7256
        utf16_output.data(), locations);
7257
  }
7258
}
7259
  #endif // SIMDUTF_SPAN
7260
7261
/**
7262
 * Convert a possibly ill-formed UTF-8 string into a UTF-16BE string, replacing
7263
 * each ill-formed subsequence with U+FFFD.
7264
 *
7265
 * Substitution follows Unicode maximal subparts (the WHATWG rule): each
7266
 * maximal subpart of an ill-formed sequence becomes one U+FFFD code unit.
7267
 * The function always succeeds. The output buffer must hold
7268
 * utf16_length_from_utf8_with_replacement(...).count char16_t. A buffer of
7269
 * `length` char16_t is always large enough. Pass that result to the overload
7270
 * that takes it, so the recorded error locations are not discovered again.
7271
 *
7272
 * This function is not BOM-aware.
7273
 *
7274
 * @param input         the UTF-8 string to convert
7275
 * @param length        the length of the string in bytes
7276
 * @param utf16_output  the pointer to buffer that can hold the conversion
7277
 * result
7278
 * @return the number of written char16_t
7279
 */
7280
simdutf_warn_unused size_t convert_utf8_to_utf16be_with_replacement(
7281
    const char *input, size_t length, char16_t *utf16_output) noexcept;
7282
  #if SIMDUTF_SPAN
7283
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7284
convert_utf8_to_utf16be_with_replacement(
7285
    const detail::input_span_of_byte_like auto &utf8_input,
7286
    std::span<char16_t> utf16_output) noexcept {
7287
    #if SIMDUTF_CPLUSPLUS23
7288
  if consteval {
7289
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::BIG>(
7290
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7291
  } else
7292
    #endif
7293
  {
7294
    return convert_utf8_to_utf16be_with_replacement(
7295
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7296
        utf16_output.data());
7297
  }
7298
}
7299
  #endif // SIMDUTF_SPAN
7300
7301
/**
7302
 * Convert a possibly ill-formed UTF-8 string into a UTF-16BE string, using
7303
 * error locations already computed for this same input. See
7304
 * convert_utf8_to_utf16_with_replacement.
7305
 *
7306
 * @param input         the UTF-8 string to convert
7307
 * @param length        the length of the string in bytes
7308
 * @param utf16_output  the pointer to buffer that can hold the conversion
7309
 * result
7310
 * @param locations     result of utf16_length_from_utf8_with_replacement
7311
 * @return the number of written char16_t
7312
 */
7313
simdutf_warn_unused size_t convert_utf8_to_utf16be_with_replacement(
7314
    const char *input, size_t length, char16_t *utf16_output,
7315
    const utf8_to_utf16_result &locations) noexcept;
7316
  #if SIMDUTF_SPAN
7317
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7318
convert_utf8_to_utf16be_with_replacement(
7319
    const detail::input_span_of_byte_like auto &utf8_input,
7320
    std::span<char16_t> utf16_output,
7321
    const utf8_to_utf16_result &locations) noexcept {
7322
    #if SIMDUTF_CPLUSPLUS23
7323
  if consteval {
7324
    (void)locations;
7325
    return scalar::utf8_to_utf16::convert_with_replacement<endianness::BIG>(
7326
        utf8_input.data(), utf8_input.size(), utf16_output.data());
7327
  } else
7328
    #endif
7329
  {
7330
    return convert_utf8_to_utf16be_with_replacement(
7331
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7332
        utf16_output.data(), locations);
7333
  }
7334
}
7335
  #endif // SIMDUTF_SPAN
7336
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7337
7338
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7339
/**
7340
 * Convert possibly broken UTF-8 string into UTF-32 string.
7341
 *
7342
 * During the conversion also validation of the input string is done.
7343
 * This function is suitable to work with inputs from untrusted sources.
7344
 *
7345
 * @param input         the UTF-8 string to convert
7346
 * @param length        the length of the string in bytes
7347
 * @param utf32_output  the pointer to buffer that can hold conversion result
7348
 * @return the number of written char32_t; 0 if the input was not valid UTF-8
7349
 * string
7350
 */
7351
simdutf_warn_unused size_t convert_utf8_to_utf32(
7352
    const char *input, size_t length, char32_t *utf32_output) noexcept;
7353
  #if SIMDUTF_SPAN
7354
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7355
convert_utf8_to_utf32(const detail::input_span_of_byte_like auto &utf8_input,
7356
                      std::span<char32_t> utf32_output) noexcept {
7357
    #if SIMDUTF_CPLUSPLUS23
7358
  if consteval {
7359
    return scalar::utf8_to_utf32::convert(utf8_input.data(), utf8_input.size(),
7360
                                          utf32_output.data());
7361
  } else
7362
    #endif
7363
  {
7364
    return convert_utf8_to_utf32(
7365
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7366
        utf32_output.data());
7367
  }
7368
}
7369
  #endif // SIMDUTF_SPAN
7370
7371
/**
7372
 * Convert possibly broken UTF-8 string into UTF-32 string and stop on error.
7373
 *
7374
 * During the conversion also validation of the input string is done.
7375
 * This function is suitable to work with inputs from untrusted sources.
7376
 *
7377
 * @param input         the UTF-8 string to convert
7378
 * @param length        the length of the string in bytes
7379
 * @param utf32_output  the pointer to buffer that can hold conversion result
7380
 * @return a result pair struct (of type simdutf::result containing the two
7381
 * fields error and count) with an error code and either position of the error
7382
 * (in the input in code units) if any, or the number of char32_t written if
7383
 * successful.
7384
 */
7385
simdutf_warn_unused result convert_utf8_to_utf32_with_errors(
7386
    const char *input, size_t length, char32_t *utf32_output) noexcept;
7387
  #if SIMDUTF_SPAN
7388
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
7389
convert_utf8_to_utf32_with_errors(
7390
    const detail::input_span_of_byte_like auto &utf8_input,
7391
    std::span<char32_t> utf32_output) noexcept {
7392
    #if SIMDUTF_CPLUSPLUS23
7393
  if consteval {
7394
    return scalar::utf8_to_utf32::convert_with_errors(
7395
        utf8_input.data(), utf8_input.size(), utf32_output.data());
7396
  } else
7397
    #endif
7398
  {
7399
    return convert_utf8_to_utf32_with_errors(
7400
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size(),
7401
        utf32_output.data());
7402
  }
7403
}
7404
  #endif // SIMDUTF_SPAN
7405
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7406
7407
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
7408
/**
7409
 * Convert valid UTF-8 string into latin1 string.
7410
 *
7411
 * This function assumes that the input string is valid UTF-8 and that it can be
7412
 * represented as Latin1. If you violate this assumption, the result is
7413
 * implementation defined and may include system-dependent behavior such as
7414
 * crashes.
7415
 *
7416
 * This function is for expert users only and not part of our public API. Use
7417
 * convert_utf8_to_latin1 instead. The function may be removed from the library
7418
 * in the future.
7419
 *
7420
 * This function is not BOM-aware.
7421
 *
7422
 * @param input         the UTF-8 string to convert
7423
 * @param length        the length of the string in bytes
7424
 * @param latin1_output  the pointer to buffer that can hold conversion result
7425
 * @return the number of written char; 0 if the input was not valid UTF-8 string
7426
 */
7427
simdutf_warn_unused size_t convert_valid_utf8_to_latin1(
7428
    const char *input, size_t length, char *latin1_output) noexcept;
7429
  #if SIMDUTF_SPAN
7430
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7431
convert_valid_utf8_to_latin1(
7432
    const detail::input_span_of_byte_like auto &valid_utf8_input,
7433
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
7434
    #if SIMDUTF_CPLUSPLUS23
7435
  if consteval {
7436
    return scalar::utf8_to_latin1::convert_valid(
7437
        valid_utf8_input.data(), valid_utf8_input.size(), latin1_output.data());
7438
  } else
7439
    #endif
7440
  {
7441
    return convert_valid_utf8_to_latin1(
7442
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7443
        valid_utf8_input.size(), latin1_output.data());
7444
  }
7445
}
7446
  #endif // SIMDUTF_SPAN
7447
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
7448
7449
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7450
/**
7451
 * Using native endianness, convert valid UTF-8 string into a UTF-16 string.
7452
 *
7453
 * This function assumes that the input string is valid UTF-8.
7454
 *
7455
 * @param input         the UTF-8 string to convert
7456
 * @param length        the length of the string in bytes
7457
 * @param utf16_buffer  the pointer to buffer that can hold conversion result
7458
 * @return the number of written char16_t
7459
 */
7460
simdutf_warn_unused size_t convert_valid_utf8_to_utf16(
7461
    const char *input, size_t length, char16_t *utf16_buffer) noexcept;
7462
  #if SIMDUTF_SPAN
7463
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7464
convert_valid_utf8_to_utf16(
7465
    const detail::input_span_of_byte_like auto &valid_utf8_input,
7466
    std::span<char16_t> utf16_output) noexcept {
7467
    #if SIMDUTF_CPLUSPLUS23
7468
  if consteval {
7469
    return scalar::utf8_to_utf16::convert_valid<endianness::NATIVE>(
7470
        valid_utf8_input.data(), valid_utf8_input.size(), utf16_output.data());
7471
  } else
7472
    #endif
7473
  {
7474
    return convert_valid_utf8_to_utf16(
7475
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7476
        valid_utf8_input.size(), utf16_output.data());
7477
  }
7478
}
7479
  #endif // SIMDUTF_SPAN
7480
7481
/**
7482
 * Convert valid UTF-8 string into UTF-16LE string.
7483
 *
7484
 * This function assumes that the input string is valid UTF-8.
7485
 *
7486
 * @param input         the UTF-8 string to convert
7487
 * @param length        the length of the string in bytes
7488
 * @param utf16_buffer  the pointer to buffer that can hold conversion result
7489
 * @return the number of written char16_t
7490
 */
7491
simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(
7492
    const char *input, size_t length, char16_t *utf16_buffer) noexcept;
7493
  #if SIMDUTF_SPAN
7494
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7495
convert_valid_utf8_to_utf16le(
7496
    const detail::input_span_of_byte_like auto &valid_utf8_input,
7497
    std::span<char16_t> utf16_output) noexcept {
7498
7499
    #if SIMDUTF_CPLUSPLUS23
7500
  if consteval {
7501
    return scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(
7502
        valid_utf8_input.data(), valid_utf8_input.size(), utf16_output.data());
7503
  } else
7504
    #endif
7505
  {
7506
    return convert_valid_utf8_to_utf16le(
7507
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7508
        valid_utf8_input.size(), utf16_output.data());
7509
  }
7510
}
7511
  #endif // SIMDUTF_SPAN
7512
7513
/**
7514
 * Convert valid UTF-8 string into UTF-16BE string.
7515
 *
7516
 * This function assumes that the input string is valid UTF-8.
7517
 *
7518
 * @param input         the UTF-8 string to convert
7519
 * @param length        the length of the string in bytes
7520
 * @param utf16_buffer  the pointer to buffer that can hold conversion result
7521
 * @return the number of written char16_t
7522
 */
7523
simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(
7524
    const char *input, size_t length, char16_t *utf16_buffer) noexcept;
7525
  #if SIMDUTF_SPAN
7526
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7527
convert_valid_utf8_to_utf16be(
7528
    const detail::input_span_of_byte_like auto &valid_utf8_input,
7529
    std::span<char16_t> utf16_output) noexcept {
7530
    #if SIMDUTF_CPLUSPLUS23
7531
  if consteval {
7532
    return scalar::utf8_to_utf16::convert_valid<endianness::BIG>(
7533
        valid_utf8_input.data(), valid_utf8_input.size(), utf16_output.data());
7534
  } else
7535
    #endif
7536
  {
7537
    return convert_valid_utf8_to_utf16be(
7538
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7539
        valid_utf8_input.size(), utf16_output.data());
7540
  }
7541
}
7542
  #endif // SIMDUTF_SPAN
7543
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7544
7545
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7546
/**
7547
 * Convert valid UTF-8 string into UTF-32 string.
7548
 *
7549
 * This function assumes that the input string is valid UTF-8.
7550
 *
7551
 * @param input         the UTF-8 string to convert
7552
 * @param length        the length of the string in bytes
7553
 * @param utf32_buffer  the pointer to buffer that can hold conversion result
7554
 * @return the number of written char32_t
7555
 */
7556
simdutf_warn_unused size_t convert_valid_utf8_to_utf32(
7557
    const char *input, size_t length, char32_t *utf32_buffer) noexcept;
7558
  #if SIMDUTF_SPAN
7559
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7560
convert_valid_utf8_to_utf32(
7561
    const detail::input_span_of_byte_like auto &valid_utf8_input,
7562
    std::span<char32_t> utf32_output) noexcept {
7563
    #if SIMDUTF_CPLUSPLUS23
7564
  if consteval {
7565
    return scalar::utf8_to_utf32::convert_valid(
7566
        valid_utf8_input.data(), valid_utf8_input.size(), utf32_output.data());
7567
  } else
7568
    #endif
7569
  {
7570
    return convert_valid_utf8_to_utf32(
7571
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7572
        valid_utf8_input.size(), utf32_output.data());
7573
  }
7574
}
7575
  #endif // SIMDUTF_SPAN
7576
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7577
7578
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
7579
/**
7580
 * Return the number of bytes that this Latin1 string would require in UTF-8
7581
 * format.
7582
 *
7583
 * @param input         the Latin1 string to convert
7584
 * @param length        the length of the string bytes
7585
 * @return the number of bytes required to encode the Latin1 string as UTF-8
7586
 */
7587
simdutf_warn_unused size_t utf8_length_from_latin1(const char *input,
7588
                                                   size_t length) noexcept;
7589
  #if SIMDUTF_SPAN
7590
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7591
utf8_length_from_latin1(
7592
    const detail::input_span_of_byte_like auto &latin1_input) noexcept {
7593
    #if SIMDUTF_CPLUSPLUS23
7594
  if consteval {
7595
    return scalar::latin1_to_utf8::utf8_length_from_latin1(latin1_input.data(),
7596
                                                           latin1_input.size());
7597
  } else
7598
    #endif
7599
  {
7600
    return utf8_length_from_latin1(
7601
        reinterpret_cast<const char *>(latin1_input.data()),
7602
        latin1_input.size());
7603
  }
7604
}
7605
  #endif // SIMDUTF_SPAN
7606
7607
/**
7608
 * Compute the number of bytes that this UTF-8 string would require in Latin1
7609
 * format.
7610
 *
7611
 * This function does not validate the input. It is acceptable to pass invalid
7612
 * UTF-8 strings but in such cases the result is implementation defined.
7613
 *
7614
 * This function is not BOM-aware.
7615
 *
7616
 * @param input         the UTF-8 string to convert
7617
 * @param length        the length of the string in byte
7618
 * @return the number of bytes required to encode the UTF-8 string as Latin1
7619
 */
7620
simdutf_warn_unused size_t latin1_length_from_utf8(const char *input,
7621
                                                   size_t length) noexcept;
7622
  #if SIMDUTF_SPAN
7623
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7624
latin1_length_from_utf8(
7625
    const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept {
7626
    #if SIMDUTF_CPLUSPLUS23
7627
  if consteval {
7628
    return scalar::utf8::count_code_points(valid_utf8_input.data(),
7629
                                           valid_utf8_input.size());
7630
  } else
7631
    #endif
7632
  {
7633
    return latin1_length_from_utf8(
7634
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7635
        valid_utf8_input.size());
7636
  }
7637
}
7638
  #endif // SIMDUTF_SPAN
7639
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
7640
7641
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7642
/**
7643
 * Compute the number of 2-byte code units that this UTF-8 string would require
7644
 * in UTF-16LE format.
7645
 *
7646
 * This function does not validate the input. It is acceptable to pass invalid
7647
 * UTF-8 strings but in such cases the result is implementation defined.
7648
 *
7649
 * This function is not BOM-aware.
7650
 *
7651
 * @param input         the UTF-8 string to process
7652
 * @param length        the length of the string in bytes
7653
 * @return the number of char16_t code units required to encode the UTF-8 string
7654
 * as UTF-16LE
7655
 */
7656
simdutf_warn_unused size_t utf16_length_from_utf8(const char *input,
7657
                                                  size_t length) noexcept;
7658
  #if SIMDUTF_SPAN
7659
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7660
utf16_length_from_utf8(
7661
    const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept {
7662
    #if SIMDUTF_CPLUSPLUS23
7663
  if consteval {
7664
    return scalar::utf8::utf16_length_from_utf8(valid_utf8_input.data(),
7665
                                                valid_utf8_input.size());
7666
  } else
7667
    #endif
7668
  {
7669
    return utf16_length_from_utf8(
7670
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7671
        valid_utf8_input.size());
7672
  }
7673
}
7674
  #endif // SIMDUTF_SPAN
7675
7676
/**
7677
 * Compute the number of char16_t that this UTF-8 string requires in UTF-16
7678
 * when ill-formed subsequences are replaced by U+FFFD, and record where those
7679
 * subsequences are.
7680
 *
7681
 * Substitution follows Unicode maximal subparts (the WHATWG rule): each
7682
 * maximal subpart becomes one U+FFFD. count is always the number of char16_t
7683
 * that convert_utf8_to_utf16_with_replacement writes, and it is at most
7684
 * `length`. The count does not depend on endianness.
7685
 *
7686
 * error is SUCCESS when the input is valid UTF-8. Otherwise it is the error
7687
 * code of the first ill-formed sequence (HEADER_BITS, TOO_SHORT, TOO_LONG,
7688
 * OVERLONG, TOO_LARGE, or SURROGATE). count stays correct either way.
7689
 *
7690
 * error_offset holds the first error_count byte indexes, at most
7691
 * utf8_to_utf16_result::max_errors (16), counting from the start of input.
7692
 * more_errors is true when the input has further ill-formed subsequences.
7693
 * Pass the whole result to convert_utf8_to_utf16_with_replacement on these
7694
 * same bytes. A complete list (more_errors false) lets that conversion skip a
7695
 * second scan.
7696
 *
7697
 * When the input is known to be valid, utf16_length_from_utf8 is the faster
7698
 * length function: it only counts, whereas this one validates.
7699
 *
7700
 * This function is not BOM-aware.
7701
 *
7702
 * @param input         the UTF-8 string to process
7703
 * @param length        the length of the string in bytes
7704
 * @return a utf8_to_utf16_result
7705
 */
7706
simdutf_warn_unused utf8_to_utf16_result
7707
utf16_length_from_utf8_with_replacement(const char *input,
7708
                                        size_t length) noexcept;
7709
  #if SIMDUTF_SPAN
7710
simdutf_really_inline
7711
    simdutf_warn_unused simdutf_constexpr23 utf8_to_utf16_result
7712
    utf16_length_from_utf8_with_replacement(
7713
        const detail::input_span_of_byte_like auto &utf8_input) noexcept {
7714
    #if SIMDUTF_CPLUSPLUS23
7715
  if consteval {
7716
    return scalar::utf8_to_utf16::utf16_length_from_utf8_with_replacement(
7717
        utf8_input.data(), utf8_input.size());
7718
  } else
7719
    #endif
7720
  {
7721
    return utf16_length_from_utf8_with_replacement(
7722
        reinterpret_cast<const char *>(utf8_input.data()), utf8_input.size());
7723
  }
7724
}
7725
  #endif // SIMDUTF_SPAN
7726
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7727
7728
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7729
/**
7730
 * Compute the number of 4-byte code units that this UTF-8 string would require
7731
 * in UTF-32 format.
7732
 *
7733
 * This function is equivalent to count_utf8
7734
 *
7735
 * This function does not validate the input. It is acceptable to pass invalid
7736
 * UTF-8 strings but in such cases the result is implementation defined.
7737
 *
7738
 * This function is not BOM-aware.
7739
 *
7740
 * @param input         the UTF-8 string to process
7741
 * @param length        the length of the string in bytes
7742
 * @return the number of char32_t code units required to encode the UTF-8 string
7743
 * as UTF-32
7744
 */
7745
simdutf_warn_unused size_t utf32_length_from_utf8(const char *input,
7746
                                                  size_t length) noexcept;
7747
  #if SIMDUTF_SPAN
7748
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7749
utf32_length_from_utf8(
7750
    const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept {
7751
7752
    #if SIMDUTF_CPLUSPLUS23
7753
  if consteval {
7754
    return scalar::utf8::count_code_points(valid_utf8_input.data(),
7755
                                           valid_utf8_input.size());
7756
  } else
7757
    #endif
7758
  {
7759
    return utf32_length_from_utf8(
7760
        reinterpret_cast<const char *>(valid_utf8_input.data()),
7761
        valid_utf8_input.size());
7762
  }
7763
}
7764
  #endif // SIMDUTF_SPAN
7765
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
7766
7767
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7768
/**
7769
 * Using native endianness, convert possibly broken UTF-16 string into UTF-8
7770
 * string.
7771
 *
7772
 * During the conversion also validation of the input string is done.
7773
 * This function is suitable to work with inputs from untrusted sources.
7774
 *
7775
 * This function is not BOM-aware.
7776
 *
7777
 * @param input         the UTF-16 string to convert
7778
 * @param length        the length of the string in 2-byte code units (char16_t)
7779
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
7780
 * @return number of written code units; 0 if input is not a valid UTF-16LE
7781
 * string
7782
 */
7783
simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t *input,
7784
                                                 size_t length,
7785
                                                 char *utf8_buffer) noexcept;
7786
  #if SIMDUTF_SPAN
7787
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7788
convert_utf16_to_utf8(
7789
    std::span<const char16_t> utf16_input,
7790
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
7791
    #if SIMDUTF_CPLUSPLUS23
7792
  if consteval {
7793
    return scalar::utf16_to_utf8::convert<endianness::NATIVE>(
7794
        utf16_input.data(), utf16_input.size(), utf8_output.data());
7795
  } else
7796
    #endif
7797
  {
7798
    return convert_utf16_to_utf8(utf16_input.data(), utf16_input.size(),
7799
                                 reinterpret_cast<char *>(utf8_output.data()));
7800
  }
7801
}
7802
  #endif // SIMDUTF_SPAN
7803
7804
/**
7805
 * Using native endianness, convert possibly broken UTF-16 string into UTF-8
7806
 * string with output limit.
7807
 *
7808
 * We write as many characters as possible into the output buffer,
7809
 *
7810
 * During the conversion also validation of the input string is done.
7811
 * This function is suitable to work with inputs from untrusted sources.
7812
 *
7813
 * This function is not BOM-aware.
7814
 *
7815
 * Using convert_utf16_to_utf8_safe instead of convert_utf16_to_utf8 comes with
7816
 * a significant penalty in some cases, being up to three times slower,
7817
 * especially on short inputs. If you have allocated the output buffer so that
7818
 * it contains utf8_length_from_utf16(input, length) bytes, then prefer
7819
 * convert_utf16_to_utf8.
7820
 *
7821
 * @param input         the UTF-16 string to convert
7822
 * @param length        the length of the string in 16-bit code units (char16_t)
7823
 * @param utf8_output   the pointer to buffer that can hold conversion result
7824
 * @param utf8_len      the maximum output length
7825
 * @return the number of written char; 0 if conversion is not possible
7826
 */
7827
simdutf_warn_unused size_t convert_utf16_to_utf8_safe(const char16_t *input,
7828
                                                      size_t length,
7829
                                                      char *utf8_output,
7830
                                                      size_t utf8_len) noexcept;
7831
  #if SIMDUTF_SPAN
7832
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7833
convert_utf16_to_utf8_safe(
7834
    std::span<const char16_t> utf16_input,
7835
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
7836
      // implementation note: outputspan is a forwarding ref to avoid copying
7837
      // and allow both lvalues and rvalues. std::span can be copied without
7838
      // problems, but std::vector should not, and this function should accept
7839
      // both. it will allow using an owning rvalue ref (example: passing a
7840
      // temporary std::string) as output, but the user will quickly find out
7841
      // that he has no way of getting the data out of the object in that case.
7842
    #if SIMDUTF_CPLUSPLUS23
7843
  if consteval {
7844
    const full_result r =
7845
        scalar::utf16_to_utf8::convert_with_errors<endianness::NATIVE, true>(
7846
            utf16_input.data(), utf16_input.size(), utf8_output.data(),
7847
            utf8_output.size());
7848
    if (r.error != error_code::SUCCESS &&
7849
        r.error != error_code::OUTPUT_BUFFER_TOO_SMALL) {
7850
      return 0;
7851
    }
7852
    return r.output_count;
7853
  } else
7854
    #endif
7855
  {
7856
    return convert_utf16_to_utf8_safe(
7857
        utf16_input.data(), utf16_input.size(),
7858
        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
7859
  }
7860
}
7861
  #endif // SIMDUTF_SPAN
7862
7863
/**
7864
 * Convert a possibly broken UTF-16 string into a size-limited UTF-8 buffer and
7865
 * report how much input was consumed and output was written.
7866
 *
7867
 * We write as many complete characters as possible while validating the input.
7868
 * The returned error is SUCCESS if all input was consumed,
7869
 * OUTPUT_BUFFER_TOO_SMALL if the next character does not fit, or SURROGATE if
7870
 * an unpaired surrogate was found.
7871
 *
7872
 * @param input         the UTF-16 string to convert
7873
 * @param length        the length in 16-bit code units
7874
 * @param utf8_output   the pointer to the output buffer
7875
 * @param utf8_len      the maximum output length
7876
 * @return a full_result with error, input_count and output_count
7877
 */
7878
simdutf_warn_unused full_result convert_utf16_to_utf8_safe_with_details(
7879
    const char16_t *input, size_t length, char *utf8_output,
7880
    size_t utf8_len) noexcept;
7881
  #if SIMDUTF_SPAN
7882
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result
7883
convert_utf16_to_utf8_safe_with_details(
7884
    std::span<const char16_t> utf16_input,
7885
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
7886
    #if SIMDUTF_CPLUSPLUS23
7887
  if consteval {
7888
    if (utf16_input.empty()) {
7889
      return full_result(error_code::SUCCESS, 0, 0);
7890
    }
7891
    return scalar::utf16_to_utf8::convert_with_errors<endianness::NATIVE, true>(
7892
        utf16_input.data(), utf16_input.size(), utf8_output.data(),
7893
        utf8_output.size());
7894
  } else
7895
    #endif
7896
  {
7897
    return convert_utf16_to_utf8_safe_with_details(
7898
        utf16_input.data(), utf16_input.size(),
7899
        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
7900
  }
7901
}
7902
  #endif // SIMDUTF_SPAN
7903
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
7904
7905
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
7906
/**
7907
 * Using native endianness, convert possibly broken UTF-16 string into Latin1
7908
 * string.
7909
 *
7910
 * During the conversion also validation of the input string is done.
7911
 * This function is suitable to work with inputs from untrusted sources.
7912
 *
7913
 * This function is not BOM-aware.
7914
 *
7915
 * @param input         the UTF-16 string to convert
7916
 * @param length        the length of the string in 2-byte code units (char16_t)
7917
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
7918
 * @return number of written code units; 0 if input is not a valid UTF-16 string
7919
 * or if it cannot be represented as Latin1
7920
 */
7921
simdutf_warn_unused size_t convert_utf16_to_latin1(
7922
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
7923
  #if SIMDUTF_SPAN
7924
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7925
convert_utf16_to_latin1(
7926
    std::span<const char16_t> utf16_input,
7927
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
7928
    #if SIMDUTF_CPLUSPLUS23
7929
  if consteval {
7930
    return scalar::utf16_to_latin1::convert<endianness::NATIVE>(
7931
        utf16_input.data(), utf16_input.size(), latin1_output.data());
7932
  } else
7933
    #endif
7934
  {
7935
    return convert_utf16_to_latin1(
7936
        utf16_input.data(), utf16_input.size(),
7937
        reinterpret_cast<char *>(latin1_output.data()));
7938
  }
7939
}
7940
  #endif // SIMDUTF_SPAN
7941
7942
/**
7943
 * Convert possibly broken UTF-16LE string into Latin1 string.
7944
 * If the string cannot be represented as Latin1, an error
7945
 * is returned.
7946
 *
7947
 * During the conversion also validation of the input string is done.
7948
 * This function is suitable to work with inputs from untrusted sources.
7949
 *
7950
 * This function is not BOM-aware.
7951
 *
7952
 * @param input         the UTF-16LE string to convert
7953
 * @param length        the length of the string in 2-byte code units (char16_t)
7954
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
7955
 * @return number of written code units; 0 if input is not a valid UTF-16LE
7956
 * string or if it cannot be represented as Latin1
7957
 */
7958
simdutf_warn_unused size_t convert_utf16le_to_latin1(
7959
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
7960
  #if SIMDUTF_SPAN
7961
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7962
convert_utf16le_to_latin1(
7963
    std::span<const char16_t> utf16_input,
7964
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
7965
    #if SIMDUTF_CPLUSPLUS23
7966
  if consteval {
7967
    return scalar::utf16_to_latin1::convert<endianness::LITTLE>(
7968
        utf16_input.data(), utf16_input.size(), latin1_output.data());
7969
  } else
7970
    #endif
7971
  {
7972
    return convert_utf16le_to_latin1(
7973
        utf16_input.data(), utf16_input.size(),
7974
        reinterpret_cast<char *>(latin1_output.data()));
7975
  }
7976
}
7977
  #endif // SIMDUTF_SPAN
7978
7979
/**
7980
 * Convert possibly broken UTF-16BE string into Latin1 string.
7981
 *
7982
 * During the conversion also validation of the input string is done.
7983
 * This function is suitable to work with inputs from untrusted sources.
7984
 *
7985
 * This function is not BOM-aware.
7986
 *
7987
 * @param input         the UTF-16BE string to convert
7988
 * @param length        the length of the string in 2-byte code units (char16_t)
7989
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
7990
 * @return number of written code units; 0 if input is not a valid UTF-16BE
7991
 * string or if it cannot be represented as Latin1
7992
 */
7993
simdutf_warn_unused size_t convert_utf16be_to_latin1(
7994
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
7995
  #if SIMDUTF_SPAN
7996
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
7997
convert_utf16be_to_latin1(
7998
    std::span<const char16_t> utf16_input,
7999
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8000
    #if SIMDUTF_CPLUSPLUS23
8001
  if consteval {
8002
    return scalar::utf16_to_latin1::convert<endianness::BIG>(
8003
        utf16_input.data(), utf16_input.size(), latin1_output.data());
8004
  } else
8005
    #endif
8006
  {
8007
    return convert_utf16be_to_latin1(
8008
        utf16_input.data(), utf16_input.size(),
8009
        reinterpret_cast<char *>(latin1_output.data()));
8010
  }
8011
}
8012
  #endif // SIMDUTF_SPAN
8013
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
8014
8015
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8016
/**
8017
 * Convert possibly broken UTF-16LE string into UTF-8 string.
8018
 *
8019
 * During the conversion also validation of the input string is done.
8020
 * This function is suitable to work with inputs from untrusted sources.
8021
 *
8022
 * This function is not BOM-aware.
8023
 *
8024
 * @param input         the UTF-16LE string to convert
8025
 * @param length        the length of the string in 2-byte code units (char16_t)
8026
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8027
 * @return number of written code units; 0 if input is not a valid UTF-16LE
8028
 * string
8029
 */
8030
simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t *input,
8031
                                                   size_t length,
8032
                                                   char *utf8_buffer) noexcept;
8033
  #if SIMDUTF_SPAN
8034
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8035
convert_utf16le_to_utf8(
8036
    std::span<const char16_t> utf16_input,
8037
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8038
    #if SIMDUTF_CPLUSPLUS23
8039
  if consteval {
8040
    return scalar::utf16_to_utf8::convert<endianness::LITTLE>(
8041
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8042
  } else
8043
    #endif
8044
  {
8045
    return convert_utf16le_to_utf8(
8046
        utf16_input.data(), utf16_input.size(),
8047
        reinterpret_cast<char *>(utf8_output.data()));
8048
  }
8049
}
8050
  #endif // SIMDUTF_SPAN
8051
8052
/**
8053
 * Convert possibly broken UTF-16BE string into UTF-8 string.
8054
 *
8055
 * During the conversion also validation of the input string is done.
8056
 * This function is suitable to work with inputs from untrusted sources.
8057
 *
8058
 * This function is not BOM-aware.
8059
 *
8060
 * @param input         the UTF-16BE string to convert
8061
 * @param length        the length of the string in 2-byte code units (char16_t)
8062
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8063
 * @return number of written code units; 0 if input is not a valid UTF-16LE
8064
 * string
8065
 */
8066
simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t *input,
8067
                                                   size_t length,
8068
                                                   char *utf8_buffer) noexcept;
8069
  #if SIMDUTF_SPAN
8070
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8071
convert_utf16be_to_utf8(
8072
    std::span<const char16_t> utf16_input,
8073
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8074
    #if SIMDUTF_CPLUSPLUS23
8075
  if consteval {
8076
    return scalar::utf16_to_utf8::convert<endianness::BIG>(
8077
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8078
  } else
8079
    #endif
8080
  {
8081
    return convert_utf16be_to_utf8(
8082
        utf16_input.data(), utf16_input.size(),
8083
        reinterpret_cast<char *>(utf8_output.data()));
8084
  }
8085
}
8086
  #endif // SIMDUTF_SPAN
8087
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8088
8089
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
8090
/**
8091
 * Using native endianness, convert possibly broken UTF-16 string into Latin1
8092
 * string.
8093
 *
8094
 * During the conversion also validation of the input string is done.
8095
 * This function is suitable to work with inputs from untrusted sources.
8096
 * This function is not BOM-aware.
8097
 *
8098
 * @param input         the UTF-16 string to convert
8099
 * @param length        the length of the string in 2-byte code units (char16_t)
8100
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8101
 * @return a result pair struct (of type simdutf::result containing the two
8102
 * fields error and count) with an error code and either position of the error
8103
 * (in the input in code units) if any, or the number of char written if
8104
 * successful.
8105
 */
8106
simdutf_warn_unused result convert_utf16_to_latin1_with_errors(
8107
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8108
  #if SIMDUTF_SPAN
8109
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8110
convert_utf16_to_latin1_with_errors(
8111
    std::span<const char16_t> utf16_input,
8112
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8113
    #if SIMDUTF_CPLUSPLUS23
8114
  if consteval {
8115
    return scalar::utf16_to_latin1::convert_with_errors<endianness::NATIVE>(
8116
        utf16_input.data(), utf16_input.size(), latin1_output.data());
8117
  } else
8118
    #endif
8119
  {
8120
    return convert_utf16_to_latin1_with_errors(
8121
        utf16_input.data(), utf16_input.size(),
8122
        reinterpret_cast<char *>(latin1_output.data()));
8123
  }
8124
}
8125
  #endif // SIMDUTF_SPAN
8126
8127
/**
8128
 * Convert possibly broken UTF-16LE string into Latin1 string.
8129
 *
8130
 * During the conversion also validation of the input string is done.
8131
 * This function is suitable to work with inputs from untrusted sources.
8132
 * This function is not BOM-aware.
8133
 *
8134
 * @param input         the UTF-16LE string to convert
8135
 * @param length        the length of the string in 2-byte code units (char16_t)
8136
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8137
 * @return a result pair struct (of type simdutf::result containing the two
8138
 * fields error and count) with an error code and either position of the error
8139
 * (in the input in code units) if any, or the number of char written if
8140
 * successful.
8141
 */
8142
simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(
8143
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8144
  #if SIMDUTF_SPAN
8145
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8146
convert_utf16le_to_latin1_with_errors(
8147
    std::span<const char16_t> utf16_input,
8148
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8149
    #if SIMDUTF_CPLUSPLUS23
8150
  if consteval {
8151
    return scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
8152
        utf16_input.data(), utf16_input.size(), latin1_output.data());
8153
  } else
8154
    #endif
8155
  {
8156
    return convert_utf16le_to_latin1_with_errors(
8157
        utf16_input.data(), utf16_input.size(),
8158
        reinterpret_cast<char *>(latin1_output.data()));
8159
  }
8160
}
8161
  #endif // SIMDUTF_SPAN
8162
8163
/**
8164
 * Convert possibly broken UTF-16BE string into Latin1 string.
8165
 * If the string cannot be represented as Latin1, an error
8166
 * is returned.
8167
 *
8168
 * During the conversion also validation of the input string is done.
8169
 * This function is suitable to work with inputs from untrusted sources.
8170
 * This function is not BOM-aware.
8171
 *
8172
 * @param input         the UTF-16BE string to convert
8173
 * @param length        the length of the string in 2-byte code units (char16_t)
8174
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8175
 * @return a result pair struct (of type simdutf::result containing the two
8176
 * fields error and count) with an error code and either position of the error
8177
 * (in the input in code units) if any, or the number of char written if
8178
 * successful.
8179
 */
8180
simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(
8181
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8182
  #if SIMDUTF_SPAN
8183
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8184
convert_utf16be_to_latin1_with_errors(
8185
    std::span<const char16_t> utf16_input,
8186
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8187
    #if SIMDUTF_CPLUSPLUS23
8188
  if consteval {
8189
    return scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
8190
        utf16_input.data(), utf16_input.size(), latin1_output.data());
8191
  } else
8192
    #endif
8193
  {
8194
    return convert_utf16be_to_latin1_with_errors(
8195
        utf16_input.data(), utf16_input.size(),
8196
        reinterpret_cast<char *>(latin1_output.data()));
8197
  }
8198
}
8199
  #endif // SIMDUTF_SPAN
8200
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
8201
8202
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8203
/**
8204
 * Using native endianness, convert possibly broken UTF-16 string into UTF-8
8205
 * string and stop on error.
8206
 *
8207
 * During the conversion also validation of the input string is done.
8208
 * This function is suitable to work with inputs from untrusted sources.
8209
 *
8210
 * This function is not BOM-aware.
8211
 *
8212
 * @param input         the UTF-16 string to convert
8213
 * @param length        the length of the string in 2-byte code units (char16_t)
8214
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8215
 * @return a result pair struct (of type simdutf::result containing the two
8216
 * fields error and count) with an error code and either position of the error
8217
 * (in the input in code units) if any, or the number of char written if
8218
 * successful.
8219
 */
8220
simdutf_warn_unused result convert_utf16_to_utf8_with_errors(
8221
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8222
  #if SIMDUTF_SPAN
8223
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8224
convert_utf16_to_utf8_with_errors(
8225
    std::span<const char16_t> utf16_input,
8226
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8227
    #if SIMDUTF_CPLUSPLUS23
8228
  if consteval {
8229
    return scalar::utf16_to_utf8::convert_with_errors<endianness::NATIVE>(
8230
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8231
  } else
8232
    #endif
8233
  {
8234
    return convert_utf16_to_utf8_with_errors(
8235
        utf16_input.data(), utf16_input.size(),
8236
        reinterpret_cast<char *>(utf8_output.data()));
8237
  }
8238
}
8239
  #endif // SIMDUTF_SPAN
8240
8241
/**
8242
 * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error.
8243
 *
8244
 * During the conversion also validation of the input string is done.
8245
 * This function is suitable to work with inputs from untrusted sources.
8246
 *
8247
 * This function is not BOM-aware.
8248
 *
8249
 * @param input         the UTF-16LE string to convert
8250
 * @param length        the length of the string in 2-byte code units (char16_t)
8251
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8252
 * @return a result pair struct (of type simdutf::result containing the two
8253
 * fields error and count) with an error code and either position of the error
8254
 * (in the input in code units) if any, or the number of char written if
8255
 * successful.
8256
 */
8257
simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(
8258
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8259
  #if SIMDUTF_SPAN
8260
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8261
convert_utf16le_to_utf8_with_errors(
8262
    std::span<const char16_t> utf16_input,
8263
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8264
    #if SIMDUTF_CPLUSPLUS23
8265
  if consteval {
8266
    return scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
8267
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8268
  } else
8269
    #endif
8270
  {
8271
    return convert_utf16le_to_utf8_with_errors(
8272
        utf16_input.data(), utf16_input.size(),
8273
        reinterpret_cast<char *>(utf8_output.data()));
8274
  }
8275
}
8276
  #endif // SIMDUTF_SPAN
8277
8278
/**
8279
 * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error.
8280
 *
8281
 * During the conversion also validation of the input string is done.
8282
 * This function is suitable to work with inputs from untrusted sources.
8283
 *
8284
 * This function is not BOM-aware.
8285
 *
8286
 * @param input         the UTF-16BE string to convert
8287
 * @param length        the length of the string in 2-byte code units (char16_t)
8288
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8289
 * @return a result pair struct (of type simdutf::result containing the two
8290
 * fields error and count) with an error code and either position of the error
8291
 * (in the input in code units) if any, or the number of char written if
8292
 * successful.
8293
 */
8294
simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(
8295
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8296
  #if SIMDUTF_SPAN
8297
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8298
convert_utf16be_to_utf8_with_errors(
8299
    std::span<const char16_t> utf16_input,
8300
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8301
    #if SIMDUTF_CPLUSPLUS23
8302
  if consteval {
8303
    return scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
8304
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8305
  } else
8306
    #endif
8307
  {
8308
    return convert_utf16be_to_utf8_with_errors(
8309
        utf16_input.data(), utf16_input.size(),
8310
        reinterpret_cast<char *>(utf8_output.data()));
8311
  }
8312
}
8313
  #endif // SIMDUTF_SPAN
8314
8315
/**
8316
 * Convert possibly broken UTF-16LE string into UTF-8 string, replacing
8317
 * unpaired surrogates with the Unicode replacement character U+FFFD.
8318
 *
8319
 * This function always succeeds: unpaired surrogates are replaced with
8320
 * U+FFFD (3 bytes in UTF-8: 0xEF 0xBF 0xBD).
8321
 *
8322
 * This function is not BOM-aware.
8323
 *
8324
 * @param input         the UTF-16LE string to convert
8325
 * @param length        the length of the string in 2-byte code units (char16_t)
8326
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8327
 * @return number of written code units
8328
 */
8329
simdutf_warn_unused size_t convert_utf16le_to_utf8_with_replacement(
8330
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8331
  #if SIMDUTF_SPAN
8332
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8333
convert_utf16le_to_utf8_with_replacement(
8334
    std::span<const char16_t> utf16_input,
8335
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8336
    #if SIMDUTF_CPLUSPLUS23
8337
  if consteval {
8338
    return scalar::utf16_to_utf8::convert_with_replacement<endianness::LITTLE>(
8339
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8340
  } else
8341
    #endif
8342
  {
8343
    return convert_utf16le_to_utf8_with_replacement(
8344
        utf16_input.data(), utf16_input.size(),
8345
        reinterpret_cast<char *>(utf8_output.data()));
8346
  }
8347
}
8348
  #endif // SIMDUTF_SPAN
8349
8350
/**
8351
 * Convert possibly broken UTF-16BE string into UTF-8 string, replacing
8352
 * unpaired surrogates with the Unicode replacement character U+FFFD.
8353
 *
8354
 * This function always succeeds: unpaired surrogates are replaced with
8355
 * U+FFFD (3 bytes in UTF-8: 0xEF 0xBF 0xBD).
8356
 *
8357
 * This function is not BOM-aware.
8358
 *
8359
 * @param input         the UTF-16BE string to convert
8360
 * @param length        the length of the string in 2-byte code units (char16_t)
8361
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8362
 * @return number of written code units
8363
 */
8364
simdutf_warn_unused size_t convert_utf16be_to_utf8_with_replacement(
8365
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8366
  #if SIMDUTF_SPAN
8367
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8368
convert_utf16be_to_utf8_with_replacement(
8369
    std::span<const char16_t> utf16_input,
8370
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8371
    #if SIMDUTF_CPLUSPLUS23
8372
  if consteval {
8373
    return scalar::utf16_to_utf8::convert_with_replacement<endianness::BIG>(
8374
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8375
  } else
8376
    #endif
8377
  {
8378
    return convert_utf16be_to_utf8_with_replacement(
8379
        utf16_input.data(), utf16_input.size(),
8380
        reinterpret_cast<char *>(utf8_output.data()));
8381
  }
8382
}
8383
  #endif // SIMDUTF_SPAN
8384
8385
/**
8386
 * Convert possibly broken UTF-16 string (native endianness) into UTF-8 string,
8387
 * replacing unpaired surrogates with the Unicode replacement character U+FFFD.
8388
 *
8389
 * This function always succeeds: unpaired surrogates are replaced with
8390
 * U+FFFD (3 bytes in UTF-8: 0xEF 0xBF 0xBD).
8391
 *
8392
 * This function is not BOM-aware.
8393
 *
8394
 * @param input         the UTF-16 string to convert
8395
 * @param length        the length of the string in 2-byte code units (char16_t)
8396
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
8397
 * @return number of written code units
8398
 */
8399
simdutf_warn_unused size_t convert_utf16_to_utf8_with_replacement(
8400
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8401
  #if SIMDUTF_SPAN
8402
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8403
convert_utf16_to_utf8_with_replacement(
8404
    std::span<const char16_t> utf16_input,
8405
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8406
    #if SIMDUTF_CPLUSPLUS23
8407
  if consteval {
8408
    return scalar::utf16_to_utf8::convert_with_replacement<endianness::NATIVE>(
8409
        utf16_input.data(), utf16_input.size(), utf8_output.data());
8410
  } else
8411
    #endif
8412
  {
8413
    return convert_utf16_to_utf8_with_replacement(
8414
        utf16_input.data(), utf16_input.size(),
8415
        reinterpret_cast<char *>(utf8_output.data()));
8416
  }
8417
}
8418
  #endif // SIMDUTF_SPAN
8419
8420
/**
8421
 * Convert a possibly broken UTF-16 string into a size-limited UTF-8 buffer,
8422
 * replacing unpaired surrogates with U+FFFD and reporting how much input was
8423
 * consumed and output was written.
8424
 *
8425
 * We write as many complete characters as possible. The returned error is
8426
 * SUCCESS if all input was consumed, or OUTPUT_BUFFER_TOO_SMALL if the next
8427
 * character or replacement does not fit.
8428
 *
8429
 * @param input         the UTF-16 string to convert
8430
 * @param length        the length in 16-bit code units
8431
 * @param utf8_output   the pointer to the output buffer
8432
 * @param utf8_len      the maximum output length
8433
 * @return a full_result with error, input_count and output_count
8434
 */
8435
simdutf_warn_unused full_result convert_utf16_to_utf8_with_replacement_safe(
8436
    const char16_t *input, size_t length, char *utf8_output,
8437
    size_t utf8_len) noexcept;
8438
  #if SIMDUTF_SPAN
8439
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result
8440
convert_utf16_to_utf8_with_replacement_safe(
8441
    std::span<const char16_t> utf16_input,
8442
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8443
    #if SIMDUTF_CPLUSPLUS23
8444
  if consteval {
8445
    return scalar::utf16_to_utf8::convert_with_replacement_safe<
8446
        endianness::NATIVE>(utf16_input.data(), utf16_input.size(),
8447
                            utf8_output.data(), utf8_output.size());
8448
  } else
8449
    #endif
8450
  {
8451
    return convert_utf16_to_utf8_with_replacement_safe(
8452
        utf16_input.data(), utf16_input.size(),
8453
        reinterpret_cast<char *>(utf8_output.data()), utf8_output.size());
8454
  }
8455
}
8456
  #endif // SIMDUTF_SPAN
8457
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8458
8459
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8460
/**
8461
 * Using native endianness, convert valid UTF-16 string into UTF-8 string.
8462
 *
8463
 * This function assumes that the input string is valid UTF-16.
8464
 *
8465
 * This function is not BOM-aware.
8466
 *
8467
 * @param input         the UTF-16 string to convert
8468
 * @param length        the length of the string in 2-byte code units (char16_t)
8469
 * @param utf8_buffer   the pointer to a buffer that can hold the conversion
8470
 * result
8471
 * @return number of written code units; 0 if conversion is not possible
8472
 */
8473
simdutf_warn_unused size_t convert_valid_utf16_to_utf8(
8474
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8475
  #if SIMDUTF_SPAN
8476
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8477
convert_valid_utf16_to_utf8(
8478
    std::span<const char16_t> valid_utf16_input,
8479
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8480
    #if SIMDUTF_CPLUSPLUS23
8481
  if consteval {
8482
    return scalar::utf16_to_utf8::convert_valid<endianness::NATIVE>(
8483
        valid_utf16_input.data(), valid_utf16_input.size(), utf8_output.data());
8484
  } else
8485
    #endif
8486
  {
8487
    return convert_valid_utf16_to_utf8(
8488
        valid_utf16_input.data(), valid_utf16_input.size(),
8489
        reinterpret_cast<char *>(utf8_output.data()));
8490
  }
8491
}
8492
  #endif // SIMDUTF_SPAN
8493
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8494
8495
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
8496
/**
8497
 * Using native endianness, convert UTF-16 string into Latin1 string.
8498
 *
8499
 * This function assumes that the input string is valid UTF-16 and that it can
8500
 * be represented as Latin1. If you violate this assumption, the result is
8501
 * implementation defined and may include system-dependent behavior such as
8502
 * crashes.
8503
 *
8504
 * This function is for expert users only and not part of our public API. Use
8505
 * convert_utf16_to_latin1 instead. The function may be removed from the library
8506
 * in the future.
8507
 *
8508
 * This function is not BOM-aware.
8509
 *
8510
 * @param input         the UTF-16 string to convert
8511
 * @param length        the length of the string in 2-byte code units (char16_t)
8512
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8513
 * @return number of written code units; 0 if conversion is not possible
8514
 */
8515
simdutf_warn_unused size_t convert_valid_utf16_to_latin1(
8516
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8517
  #if SIMDUTF_SPAN
8518
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8519
convert_valid_utf16_to_latin1(
8520
    std::span<const char16_t> valid_utf16_input,
8521
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8522
    #if SIMDUTF_CPLUSPLUS23
8523
  if consteval {
8524
    return scalar::utf16_to_latin1::convert_valid_impl<endianness::NATIVE>(
8525
        detail::constexpr_cast_ptr<uint16_t>(valid_utf16_input.data()),
8526
        valid_utf16_input.size(),
8527
        detail::constexpr_cast_writeptr<char>(latin1_output.data()));
8528
  } else
8529
    #endif
8530
  {
8531
    return convert_valid_utf16_to_latin1(
8532
        valid_utf16_input.data(), valid_utf16_input.size(),
8533
        reinterpret_cast<char *>(latin1_output.data()));
8534
  }
8535
}
8536
  #endif // SIMDUTF_SPAN
8537
8538
/**
8539
 * Convert valid UTF-16LE string into Latin1 string.
8540
 *
8541
 * This function assumes that the input string is valid UTF-16LE and that it can
8542
 * be represented as Latin1. If you violate this assumption, the result is
8543
 * implementation defined and may include system-dependent behavior such as
8544
 * crashes.
8545
 *
8546
 * This function is for expert users only and not part of our public API. Use
8547
 * convert_utf16le_to_latin1 instead. The function may be removed from the
8548
 * library in the future.
8549
 *
8550
 * This function is not BOM-aware.
8551
 *
8552
 * @param input         the UTF-16LE string to convert
8553
 * @param length        the length of the string in 2-byte code units (char16_t)
8554
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8555
 * @return number of written code units; 0 if conversion is not possible
8556
 */
8557
simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(
8558
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8559
  #if SIMDUTF_SPAN
8560
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused size_t
8561
convert_valid_utf16le_to_latin1(
8562
    std::span<const char16_t> valid_utf16_input,
8563
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8564
    #if SIMDUTF_CPLUSPLUS23
8565
  if consteval {
8566
    return scalar::utf16_to_latin1::convert_valid_impl<endianness::LITTLE>(
8567
        detail::constexpr_cast_ptr<uint16_t>(valid_utf16_input.data()),
8568
        valid_utf16_input.size(),
8569
        detail::constexpr_cast_writeptr<char>(latin1_output.data()));
8570
  } else
8571
    #endif
8572
  {
8573
    return convert_valid_utf16le_to_latin1(
8574
        valid_utf16_input.data(), valid_utf16_input.size(),
8575
        reinterpret_cast<char *>(latin1_output.data()));
8576
  }
8577
}
8578
  #endif // SIMDUTF_SPAN
8579
8580
/**
8581
 * Convert valid UTF-16BE string into Latin1 string.
8582
 *
8583
 * This function assumes that the input string is valid UTF-16BE and that it can
8584
 * be represented as Latin1. If you violate this assumption, the result is
8585
 * implementation defined and may include system-dependent behavior such as
8586
 * crashes.
8587
 *
8588
 * This function is for expert users only and not part of our public API. Use
8589
 * convert_utf16be_to_latin1 instead. The function may be removed from the
8590
 * library in the future.
8591
 *
8592
 * This function is not BOM-aware.
8593
 *
8594
 * @param input         the UTF-16BE string to convert
8595
 * @param length        the length of the string in 2-byte code units (char16_t)
8596
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
8597
 * @return number of written code units; 0 if conversion is not possible
8598
 */
8599
simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(
8600
    const char16_t *input, size_t length, char *latin1_buffer) noexcept;
8601
  #if SIMDUTF_SPAN
8602
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused size_t
8603
convert_valid_utf16be_to_latin1(
8604
    std::span<const char16_t> valid_utf16_input,
8605
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
8606
    #if SIMDUTF_CPLUSPLUS23
8607
  if consteval {
8608
    return scalar::utf16_to_latin1::convert_valid_impl<endianness::BIG>(
8609
        detail::constexpr_cast_ptr<uint16_t>(valid_utf16_input.data()),
8610
        valid_utf16_input.size(),
8611
        detail::constexpr_cast_writeptr<char>(latin1_output.data()));
8612
  } else
8613
    #endif
8614
  {
8615
    return convert_valid_utf16be_to_latin1(
8616
        valid_utf16_input.data(), valid_utf16_input.size(),
8617
        reinterpret_cast<char *>(latin1_output.data()));
8618
  }
8619
}
8620
  #endif // SIMDUTF_SPAN
8621
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
8622
8623
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8624
/**
8625
 * Convert valid UTF-16LE string into UTF-8 string.
8626
 *
8627
 * This function assumes that the input string is valid UTF-16LE
8628
 *
8629
 * This function is not BOM-aware.
8630
 *
8631
 * @param input         the UTF-16LE string to convert
8632
 * @param length        the length of the string in 2-byte code units (char16_t)
8633
 * @param utf8_buffer   the pointer to a buffer that can hold the conversion
8634
 * result
8635
 * @return number of written code units; 0 if conversion is not possible
8636
 */
8637
simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(
8638
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8639
  #if SIMDUTF_SPAN
8640
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8641
convert_valid_utf16le_to_utf8(
8642
    std::span<const char16_t> valid_utf16_input,
8643
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8644
    #if SIMDUTF_CPLUSPLUS23
8645
  if consteval {
8646
    return scalar::utf16_to_utf8::convert_valid<endianness::NATIVE>(
8647
        valid_utf16_input.data(), valid_utf16_input.size(), utf8_output.data());
8648
  } else
8649
    #endif
8650
  {
8651
    return convert_valid_utf16le_to_utf8(
8652
        valid_utf16_input.data(), valid_utf16_input.size(),
8653
        reinterpret_cast<char *>(utf8_output.data()));
8654
  }
8655
}
8656
  #endif // SIMDUTF_SPAN
8657
8658
/**
8659
 * Convert valid UTF-16BE string into UTF-8 string.
8660
 *
8661
 * This function assumes that the input string is valid UTF-16BE.
8662
 *
8663
 * This function is not BOM-aware.
8664
 *
8665
 * @param input         the UTF-16BE string to convert
8666
 * @param length        the length of the string in 2-byte code units (char16_t)
8667
 * @param utf8_buffer   the pointer to a buffer that can hold the conversion
8668
 * result
8669
 * @return number of written code units; 0 if conversion is not possible
8670
 */
8671
simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(
8672
    const char16_t *input, size_t length, char *utf8_buffer) noexcept;
8673
  #if SIMDUTF_SPAN
8674
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8675
convert_valid_utf16be_to_utf8(
8676
    std::span<const char16_t> valid_utf16_input,
8677
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
8678
    #if SIMDUTF_CPLUSPLUS23
8679
  if consteval {
8680
    return scalar::utf16_to_utf8::convert_valid<endianness::BIG>(
8681
        valid_utf16_input.data(), valid_utf16_input.size(), utf8_output.data());
8682
  } else
8683
    #endif
8684
  {
8685
    return convert_valid_utf16be_to_utf8(
8686
        valid_utf16_input.data(), valid_utf16_input.size(),
8687
        reinterpret_cast<char *>(utf8_output.data()));
8688
  }
8689
}
8690
  #endif // SIMDUTF_SPAN
8691
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
8692
8693
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
8694
/**
8695
 * Using native endianness, convert possibly broken UTF-16 string into UTF-32
8696
 * string.
8697
 *
8698
 * During the conversion also validation of the input string is done.
8699
 * This function is suitable to work with inputs from untrusted sources.
8700
 *
8701
 * This function is not BOM-aware.
8702
 *
8703
 * @param input         the UTF-16 string to convert
8704
 * @param length        the length of the string in 2-byte code units (char16_t)
8705
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8706
 * @return number of written code units; 0 if input is not a valid UTF-16LE
8707
 * string
8708
 */
8709
simdutf_warn_unused size_t convert_utf16_to_utf32(
8710
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8711
  #if SIMDUTF_SPAN
8712
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8713
convert_utf16_to_utf32(std::span<const char16_t> utf16_input,
8714
                       std::span<char32_t> utf32_output) noexcept {
8715
8716
    #if SIMDUTF_CPLUSPLUS23
8717
  if consteval {
8718
    return scalar::utf16_to_utf32::convert<endianness::NATIVE>(
8719
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8720
  } else
8721
    #endif
8722
  {
8723
    return convert_utf16_to_utf32(utf16_input.data(), utf16_input.size(),
8724
                                  utf32_output.data());
8725
  }
8726
}
8727
  #endif // SIMDUTF_SPAN
8728
8729
/**
8730
 * Convert possibly broken UTF-16LE string into UTF-32 string.
8731
 *
8732
 * During the conversion also validation of the input string is done.
8733
 * This function is suitable to work with inputs from untrusted sources.
8734
 *
8735
 * This function is not BOM-aware.
8736
 *
8737
 * @param input         the UTF-16LE string to convert
8738
 * @param length        the length of the string in 2-byte code units (char16_t)
8739
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8740
 * @return number of written code units; 0 if input is not a valid UTF-16LE
8741
 * string
8742
 */
8743
simdutf_warn_unused size_t convert_utf16le_to_utf32(
8744
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8745
  #if SIMDUTF_SPAN
8746
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8747
convert_utf16le_to_utf32(std::span<const char16_t> utf16_input,
8748
                         std::span<char32_t> utf32_output) noexcept {
8749
    #if SIMDUTF_CPLUSPLUS23
8750
  if consteval {
8751
    return scalar::utf16_to_utf32::convert<endianness::LITTLE>(
8752
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8753
  } else
8754
    #endif
8755
  {
8756
    return convert_utf16le_to_utf32(utf16_input.data(), utf16_input.size(),
8757
                                    utf32_output.data());
8758
  }
8759
}
8760
  #endif // SIMDUTF_SPAN
8761
8762
/**
8763
 * Convert possibly broken UTF-16BE string into UTF-32 string.
8764
 *
8765
 * During the conversion also validation of the input string is done.
8766
 * This function is suitable to work with inputs from untrusted sources.
8767
 *
8768
 * This function is not BOM-aware.
8769
 *
8770
 * @param input         the UTF-16BE string to convert
8771
 * @param length        the length of the string in 2-byte code units (char16_t)
8772
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8773
 * @return number of written code units; 0 if input is not a valid UTF-16LE
8774
 * string
8775
 */
8776
simdutf_warn_unused size_t convert_utf16be_to_utf32(
8777
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8778
  #if SIMDUTF_SPAN
8779
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8780
convert_utf16be_to_utf32(std::span<const char16_t> utf16_input,
8781
                         std::span<char32_t> utf32_output) noexcept {
8782
    #if SIMDUTF_CPLUSPLUS23
8783
  if consteval {
8784
    return scalar::utf16_to_utf32::convert<endianness::BIG>(
8785
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8786
  } else
8787
    #endif
8788
  {
8789
    return convert_utf16be_to_utf32(utf16_input.data(), utf16_input.size(),
8790
                                    utf32_output.data());
8791
  }
8792
}
8793
  #endif // SIMDUTF_SPAN
8794
8795
/**
8796
 * Using native endianness, convert possibly broken UTF-16 string into
8797
 * UTF-32 string and stop on error.
8798
 *
8799
 * During the conversion also validation of the input string is done.
8800
 * This function is suitable to work with inputs from untrusted sources.
8801
 *
8802
 * This function is not BOM-aware.
8803
 *
8804
 * @param input         the UTF-16 string to convert
8805
 * @param length        the length of the string in 2-byte code units (char16_t)
8806
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8807
 * @return a result pair struct (of type simdutf::result containing the two
8808
 * fields error and count) with an error code and either position of the error
8809
 * (in the input in code units) if any, or the number of char32_t written if
8810
 * successful.
8811
 */
8812
simdutf_warn_unused result convert_utf16_to_utf32_with_errors(
8813
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8814
  #if SIMDUTF_SPAN
8815
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8816
convert_utf16_to_utf32_with_errors(std::span<const char16_t> utf16_input,
8817
                                   std::span<char32_t> utf32_output) noexcept {
8818
    #if SIMDUTF_CPLUSPLUS23
8819
  if consteval {
8820
    return scalar::utf16_to_utf32::convert_with_errors<endianness::NATIVE>(
8821
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8822
  } else
8823
    #endif
8824
  {
8825
    return convert_utf16_to_utf32_with_errors(
8826
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8827
  }
8828
}
8829
  #endif // SIMDUTF_SPAN
8830
8831
/**
8832
 * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error.
8833
 *
8834
 * During the conversion also validation of the input string is done.
8835
 * This function is suitable to work with inputs from untrusted sources.
8836
 *
8837
 * This function is not BOM-aware.
8838
 *
8839
 * @param input         the UTF-16LE string to convert
8840
 * @param length        the length of the string in 2-byte code units (char16_t)
8841
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8842
 * @return a result pair struct (of type simdutf::result containing the two
8843
 * fields error and count) with an error code and either position of the error
8844
 * (in the input in code units) if any, or the number of char32_t written if
8845
 * successful.
8846
 */
8847
simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(
8848
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8849
  #if SIMDUTF_SPAN
8850
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8851
convert_utf16le_to_utf32_with_errors(
8852
    std::span<const char16_t> utf16_input,
8853
    std::span<char32_t> utf32_output) noexcept {
8854
    #if SIMDUTF_CPLUSPLUS23
8855
  if consteval {
8856
    return scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
8857
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8858
  } else
8859
    #endif
8860
  {
8861
    return convert_utf16le_to_utf32_with_errors(
8862
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8863
  }
8864
}
8865
  #endif // SIMDUTF_SPAN
8866
8867
/**
8868
 * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error.
8869
 *
8870
 * During the conversion also validation of the input string is done.
8871
 * This function is suitable to work with inputs from untrusted sources.
8872
 *
8873
 * This function is not BOM-aware.
8874
 *
8875
 * @param input         the UTF-16BE string to convert
8876
 * @param length        the length of the string in 2-byte code units (char16_t)
8877
 * @param utf32_buffer   the pointer to buffer that can hold conversion result
8878
 * @return a result pair struct (of type simdutf::result containing the two
8879
 * fields error and count) with an error code and either position of the error
8880
 * (in the input in code units) if any, or the number of char32_t written if
8881
 * successful.
8882
 */
8883
simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(
8884
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8885
  #if SIMDUTF_SPAN
8886
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
8887
convert_utf16be_to_utf32_with_errors(
8888
    std::span<const char16_t> utf16_input,
8889
    std::span<char32_t> utf32_output) noexcept {
8890
    #if SIMDUTF_CPLUSPLUS23
8891
  if consteval {
8892
    return scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
8893
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8894
  } else
8895
    #endif
8896
  {
8897
    return convert_utf16be_to_utf32_with_errors(
8898
        utf16_input.data(), utf16_input.size(), utf32_output.data());
8899
  }
8900
}
8901
  #endif // SIMDUTF_SPAN
8902
8903
/**
8904
 * Using native endianness, convert valid UTF-16 string into UTF-32 string.
8905
 *
8906
 * This function assumes that the input string is valid UTF-16 (native
8907
 * endianness).
8908
 *
8909
 * This function is not BOM-aware.
8910
 *
8911
 * @param input         the UTF-16 string to convert
8912
 * @param length        the length of the string in 2-byte code units (char16_t)
8913
 * @param utf32_buffer   the pointer to a buffer that can hold the conversion
8914
 * result
8915
 * @return number of written code units; 0 if conversion is not possible
8916
 */
8917
simdutf_warn_unused size_t convert_valid_utf16_to_utf32(
8918
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8919
  #if SIMDUTF_SPAN
8920
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8921
convert_valid_utf16_to_utf32(std::span<const char16_t> valid_utf16_input,
8922
                             std::span<char32_t> utf32_output) noexcept {
8923
    #if SIMDUTF_CPLUSPLUS23
8924
  if consteval {
8925
    return scalar::utf16_to_utf32::convert_valid<endianness::NATIVE>(
8926
        valid_utf16_input.data(), valid_utf16_input.size(),
8927
        utf32_output.data());
8928
  } else
8929
    #endif
8930
  {
8931
    return convert_valid_utf16_to_utf32(valid_utf16_input.data(),
8932
                                        valid_utf16_input.size(),
8933
                                        utf32_output.data());
8934
  }
8935
}
8936
  #endif // SIMDUTF_SPAN
8937
8938
/**
8939
 * Convert valid UTF-16LE string into UTF-32 string.
8940
 *
8941
 * This function assumes that the input string is valid UTF-16LE.
8942
 *
8943
 * This function is not BOM-aware.
8944
 *
8945
 * @param input         the UTF-16LE string to convert
8946
 * @param length        the length of the string in 2-byte code units (char16_t)
8947
 * @param utf32_buffer   the pointer to a buffer that can hold the conversion
8948
 * result
8949
 * @return number of written code units; 0 if conversion is not possible
8950
 */
8951
simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(
8952
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8953
  #if SIMDUTF_SPAN
8954
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8955
convert_valid_utf16le_to_utf32(std::span<const char16_t> valid_utf16_input,
8956
                               std::span<char32_t> utf32_output) noexcept {
8957
    #if SIMDUTF_CPLUSPLUS23
8958
  if consteval {
8959
    return scalar::utf16_to_utf32::convert_valid<endianness::LITTLE>(
8960
        valid_utf16_input.data(), valid_utf16_input.size(),
8961
        utf32_output.data());
8962
  } else
8963
    #endif
8964
  {
8965
    return convert_valid_utf16le_to_utf32(valid_utf16_input.data(),
8966
                                          valid_utf16_input.size(),
8967
                                          utf32_output.data());
8968
  }
8969
}
8970
  #endif // SIMDUTF_SPAN
8971
8972
/**
8973
 * Convert valid UTF-16BE string into UTF-32 string.
8974
 *
8975
 * This function assumes that the input string is valid UTF-16LE.
8976
 *
8977
 * This function is not BOM-aware.
8978
 *
8979
 * @param input         the UTF-16BE string to convert
8980
 * @param length        the length of the string in 2-byte code units (char16_t)
8981
 * @param utf32_buffer   the pointer to a buffer that can hold the conversion
8982
 * result
8983
 * @return number of written code units; 0 if conversion is not possible
8984
 */
8985
simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(
8986
    const char16_t *input, size_t length, char32_t *utf32_buffer) noexcept;
8987
  #if SIMDUTF_SPAN
8988
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
8989
convert_valid_utf16be_to_utf32(std::span<const char16_t> valid_utf16_input,
8990
                               std::span<char32_t> utf32_output) noexcept {
8991
    #if SIMDUTF_CPLUSPLUS23
8992
  if consteval {
8993
    return scalar::utf16_to_utf32::convert_valid<endianness::BIG>(
8994
        valid_utf16_input.data(), valid_utf16_input.size(),
8995
        utf32_output.data());
8996
  } else
8997
    #endif
8998
  {
8999
    return convert_valid_utf16be_to_utf32(valid_utf16_input.data(),
9000
                                          valid_utf16_input.size(),
9001
                                          utf32_output.data());
9002
  }
9003
}
9004
  #endif // SIMDUTF_SPAN
9005
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9006
9007
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
9008
/**
9009
 * Using native endianness; Compute the number of bytes that this UTF-16
9010
 * string would require in UTF-8 format.
9011
 *
9012
 * This function does not validate the input. It is acceptable to pass invalid
9013
 * UTF-16 strings but in such cases the result is implementation defined.
9014
 *
9015
 * @param input         the UTF-16 string to convert
9016
 * @param length        the length of the string in 2-byte code units (char16_t)
9017
 * @return the number of bytes required to encode the UTF-16LE string as UTF-8
9018
 */
9019
simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t *input,
9020
                                                  size_t length) noexcept;
9021
  #if SIMDUTF_SPAN
9022
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9023
utf8_length_from_utf16(std::span<const char16_t> valid_utf16_input) noexcept {
9024
    #if SIMDUTF_CPLUSPLUS23
9025
  if consteval {
9026
    return scalar::utf16::utf8_length_from_utf16<endianness::NATIVE>(
9027
        valid_utf16_input.data(), valid_utf16_input.size());
9028
  } else
9029
    #endif
9030
  {
9031
    return utf8_length_from_utf16(valid_utf16_input.data(),
9032
                                  valid_utf16_input.size());
9033
  }
9034
}
9035
  #endif // SIMDUTF_SPAN
9036
9037
/**
9038
 * Using native endianness; compute the number of bytes that this UTF-16
9039
 * string would require in UTF-8 format even when the UTF-16LE content contains
9040
 * mismatched surrogates that have to be replaced by the replacement character
9041
 * (0xFFFD).
9042
 *
9043
 * @param input         the UTF-16 string to convert
9044
 * @param length        the length of the string in 2-byte code units (char16_t)
9045
 * @return a result pair struct (of type simdutf::result containing the two
9046
 * fields error and count) where the count is the number of bytes required to
9047
 * encode the UTF-16 string as UTF-8, and the error code is either SUCCESS or
9048
 * SURROGATE. The count is correct regardless of the error field.
9049
 * When SURROGATE is returned, it does not indicate an error in the case of this
9050
 * function: it indicates that at least one surrogate has been encountered: the
9051
 * surrogates may be matched or not (thus this function does not validate). If
9052
 * the returned error code is SUCCESS, then the input contains no surrogate, is
9053
 * in the Basic Multilingual Plane, and is necessarily valid.
9054
 */
9055
simdutf_warn_unused result utf8_length_from_utf16_with_replacement(
9056
    const char16_t *input, size_t length) noexcept;
9057
  #if SIMDUTF_SPAN
9058
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9059
utf8_length_from_utf16_with_replacement(
9060
    std::span<const char16_t> valid_utf16_input) noexcept {
9061
    #if SIMDUTF_CPLUSPLUS23
9062
  if consteval {
9063
    return scalar::utf16::utf8_length_from_utf16_with_replacement<
9064
        endianness::NATIVE>(valid_utf16_input.data(), valid_utf16_input.size());
9065
  } else
9066
    #endif
9067
  {
9068
    return utf8_length_from_utf16_with_replacement(valid_utf16_input.data(),
9069
                                                   valid_utf16_input.size());
9070
  }
9071
}
9072
  #endif // SIMDUTF_SPAN
9073
9074
/**
9075
 * Compute the number of bytes that this UTF-16LE string would require in UTF-8
9076
 * format.
9077
 *
9078
 * This function does not validate the input. It is acceptable to pass invalid
9079
 * UTF-16 strings but in such cases the result is implementation defined.
9080
 *
9081
 * @param input         the UTF-16LE string to convert
9082
 * @param length        the length of the string in 2-byte code units (char16_t)
9083
 * @return the number of bytes required to encode the UTF-16LE string as UTF-8
9084
 */
9085
simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *input,
9086
                                                    size_t length) noexcept;
9087
  #if SIMDUTF_SPAN
9088
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused size_t
9089
utf8_length_from_utf16le(std::span<const char16_t> valid_utf16_input) noexcept {
9090
    #if SIMDUTF_CPLUSPLUS23
9091
  if consteval {
9092
    return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(
9093
        valid_utf16_input.data(), valid_utf16_input.size());
9094
  } else
9095
    #endif
9096
  {
9097
    return utf8_length_from_utf16le(valid_utf16_input.data(),
9098
                                    valid_utf16_input.size());
9099
  }
9100
}
9101
  #endif // SIMDUTF_SPAN
9102
9103
/**
9104
 * Compute the number of bytes that this UTF-16BE string would require in UTF-8
9105
 * format.
9106
 *
9107
 * This function does not validate the input. It is acceptable to pass invalid
9108
 * UTF-16 strings but in such cases the result is implementation defined.
9109
 *
9110
 * @param input         the UTF-16BE string to convert
9111
 * @param length        the length of the string in 2-byte code units (char16_t)
9112
 * @return the number of bytes required to encode the UTF-16BE string as UTF-8
9113
 */
9114
simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *input,
9115
                                                    size_t length) noexcept;
9116
  #if SIMDUTF_SPAN
9117
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9118
utf8_length_from_utf16be(std::span<const char16_t> valid_utf16_input) noexcept {
9119
    #if SIMDUTF_CPLUSPLUS23
9120
  if consteval {
9121
    return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(
9122
        valid_utf16_input.data(), valid_utf16_input.size());
9123
  } else
9124
    #endif
9125
  {
9126
    return utf8_length_from_utf16be(valid_utf16_input.data(),
9127
                                    valid_utf16_input.size());
9128
  }
9129
}
9130
  #endif // SIMDUTF_SPAN
9131
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
9132
9133
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
9134
/**
9135
 * Convert possibly broken UTF-32 string into UTF-8 string.
9136
 *
9137
 * During the conversion also validation of the input string is done.
9138
 * This function is suitable to work with inputs from untrusted sources.
9139
 *
9140
 * This function is not BOM-aware.
9141
 *
9142
 * @param input         the UTF-32 string to convert
9143
 * @param length        the length of the string in 4-byte code units (char32_t)
9144
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
9145
 * @return number of written code units; 0 if input is not a valid UTF-32 string
9146
 */
9147
simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t *input,
9148
                                                 size_t length,
9149
                                                 char *utf8_buffer) noexcept;
9150
  #if SIMDUTF_SPAN
9151
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9152
convert_utf32_to_utf8(
9153
    std::span<const char32_t> utf32_input,
9154
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
9155
    #if SIMDUTF_CPLUSPLUS23
9156
  if consteval {
9157
    return scalar::utf32_to_utf8::convert(
9158
        utf32_input.data(), utf32_input.size(), utf8_output.data());
9159
  } else
9160
    #endif
9161
  {
9162
    return convert_utf32_to_utf8(utf32_input.data(), utf32_input.size(),
9163
                                 reinterpret_cast<char *>(utf8_output.data()));
9164
  }
9165
}
9166
  #endif // SIMDUTF_SPAN
9167
9168
/**
9169
 * Convert possibly broken UTF-32 string into UTF-8 string and stop on error.
9170
 *
9171
 * During the conversion also validation of the input string is done.
9172
 * This function is suitable to work with inputs from untrusted sources.
9173
 *
9174
 * This function is not BOM-aware.
9175
 *
9176
 * @param input         the UTF-32 string to convert
9177
 * @param length        the length of the string in 4-byte code units (char32_t)
9178
 * @param utf8_buffer   the pointer to buffer that can hold conversion result
9179
 * @return a result pair struct (of type simdutf::result containing the two
9180
 * fields error and count) with an error code and either position of the error
9181
 * (in the input in code units) if any, or the number of char written if
9182
 * successful.
9183
 */
9184
simdutf_warn_unused result convert_utf32_to_utf8_with_errors(
9185
    const char32_t *input, size_t length, char *utf8_buffer) noexcept;
9186
  #if SIMDUTF_SPAN
9187
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9188
convert_utf32_to_utf8_with_errors(
9189
    std::span<const char32_t> utf32_input,
9190
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
9191
    #if SIMDUTF_CPLUSPLUS23
9192
  if consteval {
9193
    return scalar::utf32_to_utf8::convert_with_errors(
9194
        utf32_input.data(), utf32_input.size(), utf8_output.data());
9195
  } else
9196
    #endif
9197
  {
9198
    return convert_utf32_to_utf8_with_errors(
9199
        utf32_input.data(), utf32_input.size(),
9200
        reinterpret_cast<char *>(utf8_output.data()));
9201
  }
9202
}
9203
  #endif // SIMDUTF_SPAN
9204
9205
/**
9206
 * Convert valid UTF-32 string into UTF-8 string.
9207
 *
9208
 * This function assumes that the input string is valid UTF-32.
9209
 *
9210
 * This function is not BOM-aware.
9211
 *
9212
 * @param input         the UTF-32 string to convert
9213
 * @param length        the length of the string in 4-byte code units (char32_t)
9214
 * @param utf8_buffer   the pointer to a buffer that can hold the conversion
9215
 * result
9216
 * @return number of written code units; 0 if conversion is not possible
9217
 */
9218
simdutf_warn_unused size_t convert_valid_utf32_to_utf8(
9219
    const char32_t *input, size_t length, char *utf8_buffer) noexcept;
9220
  #if SIMDUTF_SPAN
9221
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9222
convert_valid_utf32_to_utf8(
9223
    std::span<const char32_t> valid_utf32_input,
9224
    detail::output_span_of_byte_like auto &&utf8_output) noexcept {
9225
    #if SIMDUTF_CPLUSPLUS23
9226
  if consteval {
9227
    return scalar::utf32_to_utf8::convert_valid(
9228
        valid_utf32_input.data(), valid_utf32_input.size(), utf8_output.data());
9229
  } else
9230
    #endif
9231
  {
9232
    return convert_valid_utf32_to_utf8(
9233
        valid_utf32_input.data(), valid_utf32_input.size(),
9234
        reinterpret_cast<char *>(utf8_output.data()));
9235
  }
9236
}
9237
  #endif // SIMDUTF_SPAN
9238
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
9239
9240
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9241
/**
9242
 * Using native endianness, convert possibly broken UTF-32 string into a UTF-16
9243
 * string.
9244
 *
9245
 * During the conversion also validation of the input string is done.
9246
 * This function is suitable to work with inputs from untrusted sources.
9247
 *
9248
 * This function is not BOM-aware.
9249
 *
9250
 * @param input         the UTF-32 string to convert
9251
 * @param length        the length of the string in 4-byte code units (char32_t)
9252
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9253
 * @return number of written code units; 0 if input is not a valid UTF-32 string
9254
 */
9255
simdutf_warn_unused size_t convert_utf32_to_utf16(
9256
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9257
  #if SIMDUTF_SPAN
9258
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9259
convert_utf32_to_utf16(std::span<const char32_t> utf32_input,
9260
                       std::span<char16_t> utf16_output) noexcept {
9261
    #if SIMDUTF_CPLUSPLUS23
9262
  if consteval {
9263
    return scalar::utf32_to_utf16::convert<endianness::NATIVE>(
9264
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9265
  } else
9266
    #endif
9267
  {
9268
    return convert_utf32_to_utf16(utf32_input.data(), utf32_input.size(),
9269
                                  utf16_output.data());
9270
  }
9271
}
9272
  #endif // SIMDUTF_SPAN
9273
9274
/**
9275
 * Convert possibly broken UTF-32 string into UTF-16LE string.
9276
 *
9277
 * During the conversion also validation of the input string is done.
9278
 * This function is suitable to work with inputs from untrusted sources.
9279
 *
9280
 * This function is not BOM-aware.
9281
 *
9282
 * @param input         the UTF-32 string to convert
9283
 * @param length        the length of the string in 4-byte code units (char32_t)
9284
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9285
 * @return number of written code units; 0 if input is not a valid UTF-32 string
9286
 */
9287
simdutf_warn_unused size_t convert_utf32_to_utf16le(
9288
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9289
  #if SIMDUTF_SPAN
9290
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9291
convert_utf32_to_utf16le(std::span<const char32_t> utf32_input,
9292
                         std::span<char16_t> utf16_output) noexcept {
9293
    #if SIMDUTF_CPLUSPLUS23
9294
  if consteval {
9295
    return scalar::utf32_to_utf16::convert<endianness::LITTLE>(
9296
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9297
  } else
9298
    #endif
9299
  {
9300
    return convert_utf32_to_utf16le(utf32_input.data(), utf32_input.size(),
9301
                                    utf16_output.data());
9302
  }
9303
}
9304
  #endif // SIMDUTF_SPAN
9305
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9306
9307
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
9308
/**
9309
 * Convert possibly broken UTF-32 string into Latin1 string.
9310
 *
9311
 * During the conversion also validation of the input string is done.
9312
 * This function is suitable to work with inputs from untrusted sources.
9313
 *
9314
 * This function is not BOM-aware.
9315
 *
9316
 * @param input         the UTF-32 string to convert
9317
 * @param length        the length of the string in 4-byte code units (char32_t)
9318
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
9319
 * @return number of written code units; 0 if input is not a valid UTF-32 string
9320
 * or if it cannot be represented as Latin1
9321
 */
9322
simdutf_warn_unused size_t convert_utf32_to_latin1(
9323
    const char32_t *input, size_t length, char *latin1_buffer) noexcept;
9324
  #if SIMDUTF_SPAN
9325
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9326
convert_utf32_to_latin1(
9327
    std::span<const char32_t> utf32_input,
9328
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
9329
    #if SIMDUTF_CPLUSPLUS23
9330
  if consteval {
9331
    return scalar::utf32_to_latin1::convert(
9332
        utf32_input.data(), utf32_input.size(), latin1_output.data());
9333
  } else
9334
    #endif
9335
  {
9336
    return convert_utf32_to_latin1(
9337
        utf32_input.data(), utf32_input.size(),
9338
        reinterpret_cast<char *>(latin1_output.data()));
9339
  }
9340
}
9341
  #endif // SIMDUTF_SPAN
9342
9343
/**
9344
 * Convert possibly broken UTF-32 string into Latin1 string and stop on error.
9345
 * If the string cannot be represented as Latin1, an error is returned.
9346
 *
9347
 * During the conversion also validation of the input string is done.
9348
 * This function is suitable to work with inputs from untrusted sources.
9349
 *
9350
 * This function is not BOM-aware.
9351
 *
9352
 * @param input         the UTF-32 string to convert
9353
 * @param length        the length of the string in 4-byte code units (char32_t)
9354
 * @param latin1_buffer   the pointer to buffer that can hold conversion result
9355
 * @return a result pair struct (of type simdutf::result containing the two
9356
 * fields error and count) with an error code and either position of the error
9357
 * (in the input in code units) if any, or the number of char written if
9358
 * successful.
9359
 */
9360
simdutf_warn_unused result convert_utf32_to_latin1_with_errors(
9361
    const char32_t *input, size_t length, char *latin1_buffer) noexcept;
9362
  #if SIMDUTF_SPAN
9363
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9364
convert_utf32_to_latin1_with_errors(
9365
    std::span<const char32_t> utf32_input,
9366
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
9367
    #if SIMDUTF_CPLUSPLUS23
9368
  if consteval {
9369
    return scalar::utf32_to_latin1::convert_with_errors(
9370
        utf32_input.data(), utf32_input.size(), latin1_output.data());
9371
  } else
9372
    #endif
9373
  {
9374
    return convert_utf32_to_latin1_with_errors(
9375
        utf32_input.data(), utf32_input.size(),
9376
        reinterpret_cast<char *>(latin1_output.data()));
9377
  }
9378
}
9379
  #endif // SIMDUTF_SPAN
9380
9381
/**
9382
 * Convert valid UTF-32 string into Latin1 string.
9383
 *
9384
 * This function assumes that the input string is valid UTF-32 and that it can
9385
 * be represented as Latin1. If you violate this assumption, the result is
9386
 * implementation defined and may include system-dependent behavior such as
9387
 * crashes.
9388
 *
9389
 * This function is for expert users only and not part of our public API. Use
9390
 * convert_utf32_to_latin1 instead. The function may be removed from the library
9391
 * in the future.
9392
 *
9393
 * This function is not BOM-aware.
9394
 *
9395
 * @param input         the UTF-32 string to convert
9396
 * @param length        the length of the string in 4-byte code units (char32_t)
9397
 * @param latin1_buffer   the pointer to a buffer that can hold the conversion
9398
 * result
9399
 * @return number of written code units; 0 if conversion is not possible
9400
 */
9401
simdutf_warn_unused size_t convert_valid_utf32_to_latin1(
9402
    const char32_t *input, size_t length, char *latin1_buffer) noexcept;
9403
  #if SIMDUTF_SPAN
9404
simdutf_really_inline simdutf_constexpr23 simdutf_warn_unused size_t
9405
convert_valid_utf32_to_latin1(
9406
    std::span<const char32_t> valid_utf32_input,
9407
    detail::output_span_of_byte_like auto &&latin1_output) noexcept {
9408
    #if SIMDUTF_CPLUSPLUS23
9409
  if consteval {
9410
    return scalar::utf32_to_latin1::convert_valid(
9411
        detail::constexpr_cast_ptr<uint32_t>(valid_utf32_input.data()),
9412
        valid_utf32_input.size(),
9413
        detail::constexpr_cast_writeptr<char>(latin1_output.data()));
9414
  }
9415
    #endif
9416
  {
9417
    return convert_valid_utf32_to_latin1(
9418
        valid_utf32_input.data(), valid_utf32_input.size(),
9419
        reinterpret_cast<char *>(latin1_output.data()));
9420
  }
9421
}
9422
  #endif // SIMDUTF_SPAN
9423
9424
/**
9425
 * Compute the number of bytes that this UTF-32 string would require in Latin1
9426
 * format.
9427
 *
9428
 * This function does not validate the input. It is acceptable to pass invalid
9429
 * UTF-32 strings but in such cases the result is implementation defined.
9430
 *
9431
 * This function is not BOM-aware.
9432
 *
9433
 * @param length        the length of the string in 4-byte code units (char32_t)
9434
 * @return the number of bytes required to encode the UTF-32 string as Latin1
9435
 */
9436
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 size_t
9437
latin1_length_from_utf32(size_t length) noexcept {
9438
  return length;
9439
}
9440
9441
/**
9442
 * Compute the number of bytes that this Latin1 string would require in UTF-32
9443
 * format.
9444
 *
9445
 * @param length        the length of the string in Latin1 code units (char)
9446
 * @return the length of the string in 4-byte code units (char32_t) required to
9447
 * encode the Latin1 string as UTF-32
9448
 */
9449
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 size_t
9450
utf32_length_from_latin1(size_t length) noexcept {
9451
  return length;
9452
}
9453
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
9454
9455
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9456
/**
9457
 * Convert possibly broken UTF-32 string into UTF-16BE string.
9458
 *
9459
 * During the conversion also validation of the input string is done.
9460
 * This function is suitable to work with inputs from untrusted sources.
9461
 *
9462
 * This function is not BOM-aware.
9463
 *
9464
 * @param input         the UTF-32 string to convert
9465
 * @param length        the length of the string in 4-byte code units (char32_t)
9466
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9467
 * @return number of written code units; 0 if input is not a valid UTF-32 string
9468
 */
9469
simdutf_warn_unused size_t convert_utf32_to_utf16be(
9470
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9471
  #if SIMDUTF_SPAN
9472
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9473
convert_utf32_to_utf16be(std::span<const char32_t> utf32_input,
9474
                         std::span<char16_t> utf16_output) noexcept {
9475
    #if SIMDUTF_CPLUSPLUS23
9476
  if consteval {
9477
    return scalar::utf32_to_utf16::convert<endianness::BIG>(
9478
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9479
  } else
9480
    #endif
9481
  {
9482
    return convert_utf32_to_utf16be(utf32_input.data(), utf32_input.size(),
9483
                                    utf16_output.data());
9484
  }
9485
}
9486
  #endif // SIMDUTF_SPAN
9487
9488
/**
9489
 * Using native endianness, convert possibly broken UTF-32 string into UTF-16
9490
 * string and stop on error.
9491
 *
9492
 * During the conversion also validation of the input string is done.
9493
 * This function is suitable to work with inputs from untrusted sources.
9494
 *
9495
 * This function is not BOM-aware.
9496
 *
9497
 * @param input         the UTF-32 string to convert
9498
 * @param length        the length of the string in 4-byte code units (char32_t)
9499
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9500
 * @return a result pair struct (of type simdutf::result containing the two
9501
 * fields error and count) with an error code and either position of the error
9502
 * (in the input in code units) if any, or the number of char16_t written if
9503
 * successful.
9504
 */
9505
simdutf_warn_unused result convert_utf32_to_utf16_with_errors(
9506
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9507
  #if SIMDUTF_SPAN
9508
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9509
convert_utf32_to_utf16_with_errors(std::span<const char32_t> utf32_input,
9510
                                   std::span<char16_t> utf16_output) noexcept {
9511
    #if SIMDUTF_CPLUSPLUS23
9512
  if consteval {
9513
    return scalar::utf32_to_utf16::convert_with_errors<endianness::NATIVE>(
9514
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9515
  } else
9516
    #endif
9517
  {
9518
    return convert_utf32_to_utf16_with_errors(
9519
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9520
  }
9521
}
9522
  #endif // SIMDUTF_SPAN
9523
9524
/**
9525
 * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error.
9526
 *
9527
 * During the conversion also validation of the input string is done.
9528
 * This function is suitable to work with inputs from untrusted sources.
9529
 *
9530
 * This function is not BOM-aware.
9531
 *
9532
 * @param input         the UTF-32 string to convert
9533
 * @param length        the length of the string in 4-byte code units (char32_t)
9534
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9535
 * @return a result pair struct (of type simdutf::result containing the two
9536
 * fields error and count) with an error code and either position of the error
9537
 * (in the input in code units) if any, or the number of char16_t written if
9538
 * successful.
9539
 */
9540
simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(
9541
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9542
  #if SIMDUTF_SPAN
9543
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9544
convert_utf32_to_utf16le_with_errors(
9545
    std::span<const char32_t> utf32_input,
9546
    std::span<char16_t> utf16_output) noexcept {
9547
    #if SIMDUTF_CPLUSPLUS23
9548
  if consteval {
9549
    return scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
9550
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9551
  } else
9552
    #endif
9553
  {
9554
    return convert_utf32_to_utf16le_with_errors(
9555
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9556
  }
9557
}
9558
  #endif // SIMDUTF_SPAN
9559
9560
/**
9561
 * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error.
9562
 *
9563
 * During the conversion also validation of the input string is done.
9564
 * This function is suitable to work with inputs from untrusted sources.
9565
 *
9566
 * This function is not BOM-aware.
9567
 *
9568
 * @param input         the UTF-32 string to convert
9569
 * @param length        the length of the string in 4-byte code units (char32_t)
9570
 * @param utf16_buffer   the pointer to buffer that can hold conversion result
9571
 * @return a result pair struct (of type simdutf::result containing the two
9572
 * fields error and count) with an error code and either position of the error
9573
 * (in the input in code units) if any, or the number of char16_t written if
9574
 * successful.
9575
 */
9576
simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(
9577
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9578
  #if SIMDUTF_SPAN
9579
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
9580
convert_utf32_to_utf16be_with_errors(
9581
    std::span<const char32_t> utf32_input,
9582
    std::span<char16_t> utf16_output) noexcept {
9583
    #if SIMDUTF_CPLUSPLUS23
9584
  if consteval {
9585
    return scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
9586
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9587
  } else
9588
    #endif
9589
  {
9590
    return convert_utf32_to_utf16be_with_errors(
9591
        utf32_input.data(), utf32_input.size(), utf16_output.data());
9592
  }
9593
}
9594
  #endif // SIMDUTF_SPAN
9595
9596
/**
9597
 * Using native endianness, convert valid UTF-32 string into a UTF-16 string.
9598
 *
9599
 * This function assumes that the input string is valid UTF-32.
9600
 *
9601
 * This function is not BOM-aware.
9602
 *
9603
 * @param input         the UTF-32 string to convert
9604
 * @param length        the length of the string in 4-byte code units (char32_t)
9605
 * @param utf16_buffer   the pointer to a buffer that can hold the conversion
9606
 * result
9607
 * @return number of written code units; 0 if conversion is not possible
9608
 */
9609
simdutf_warn_unused size_t convert_valid_utf32_to_utf16(
9610
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9611
  #if SIMDUTF_SPAN
9612
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9613
convert_valid_utf32_to_utf16(std::span<const char32_t> valid_utf32_input,
9614
                             std::span<char16_t> utf16_output) noexcept {
9615
9616
    #if SIMDUTF_CPLUSPLUS23
9617
  if consteval {
9618
    return scalar::utf32_to_utf16::convert_valid<endianness::NATIVE>(
9619
        valid_utf32_input.data(), valid_utf32_input.size(),
9620
        utf16_output.data());
9621
  } else
9622
    #endif
9623
  {
9624
    return convert_valid_utf32_to_utf16(valid_utf32_input.data(),
9625
                                        valid_utf32_input.size(),
9626
                                        utf16_output.data());
9627
  }
9628
}
9629
  #endif // SIMDUTF_SPAN
9630
9631
/**
9632
 * Convert valid UTF-32 string into UTF-16LE string.
9633
 *
9634
 * This function assumes that the input string is valid UTF-32.
9635
 *
9636
 * This function is not BOM-aware.
9637
 *
9638
 * @param input         the UTF-32 string to convert
9639
 * @param length        the length of the string in 4-byte code units (char32_t)
9640
 * @param utf16_buffer   the pointer to a buffer that can hold the conversion
9641
 * result
9642
 * @return number of written code units; 0 if conversion is not possible
9643
 */
9644
simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(
9645
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9646
  #if SIMDUTF_SPAN
9647
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9648
convert_valid_utf32_to_utf16le(std::span<const char32_t> valid_utf32_input,
9649
                               std::span<char16_t> utf16_output) noexcept {
9650
    #if SIMDUTF_CPLUSPLUS23
9651
  if consteval {
9652
    return scalar::utf32_to_utf16::convert_valid<endianness::LITTLE>(
9653
        valid_utf32_input.data(), valid_utf32_input.size(),
9654
        utf16_output.data());
9655
  } else
9656
    #endif
9657
  {
9658
    return convert_valid_utf32_to_utf16le(valid_utf32_input.data(),
9659
                                          valid_utf32_input.size(),
9660
                                          utf16_output.data());
9661
  }
9662
}
9663
  #endif // SIMDUTF_SPAN
9664
9665
/**
9666
 * Convert valid UTF-32 string into UTF-16BE string.
9667
 *
9668
 * This function assumes that the input string is valid UTF-32.
9669
 *
9670
 * This function is not BOM-aware.
9671
 *
9672
 * @param input         the UTF-32 string to convert
9673
 * @param length        the length of the string in 4-byte code units (char32_t)
9674
 * @param utf16_buffer   the pointer to a buffer that can hold the conversion
9675
 * result
9676
 * @return number of written code units; 0 if conversion is not possible
9677
 */
9678
simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(
9679
    const char32_t *input, size_t length, char16_t *utf16_buffer) noexcept;
9680
  #if SIMDUTF_SPAN
9681
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9682
convert_valid_utf32_to_utf16be(std::span<const char32_t> valid_utf32_input,
9683
                               std::span<char16_t> utf16_output) noexcept {
9684
    #if SIMDUTF_CPLUSPLUS23
9685
  if consteval {
9686
    return scalar::utf32_to_utf16::convert_valid<endianness::BIG>(
9687
        valid_utf32_input.data(), valid_utf32_input.size(),
9688
        utf16_output.data());
9689
  } else
9690
    #endif
9691
  {
9692
    return convert_valid_utf32_to_utf16be(valid_utf32_input.data(),
9693
                                          valid_utf32_input.size(),
9694
                                          utf16_output.data());
9695
  }
9696
}
9697
  #endif // SIMDUTF_SPAN
9698
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9699
9700
#if SIMDUTF_FEATURE_UTF16
9701
/**
9702
 * Change the endianness of the input. Can be used to go from UTF-16LE to
9703
 * UTF-16BE or from UTF-16BE to UTF-16LE.
9704
 *
9705
 * This function does not validate the input.
9706
 *
9707
 * This function is not BOM-aware.
9708
 *
9709
 * @param input         the UTF-16 string to process
9710
 * @param length        the length of the string in 2-byte code units (char16_t)
9711
 * @param output        the pointer to a buffer that can hold the conversion
9712
 * result
9713
 */
9714
void change_endianness_utf16(const char16_t *input, size_t length,
9715
                             char16_t *output) noexcept;
9716
  #if SIMDUTF_SPAN
9717
simdutf_really_inline simdutf_constexpr23 void
9718
change_endianness_utf16(std::span<const char16_t> utf16_input,
9719
                        std::span<char16_t> utf16_output) noexcept {
9720
    #if SIMDUTF_CPLUSPLUS23
9721
  if consteval {
9722
    return scalar::utf16::change_endianness_utf16(
9723
        utf16_input.data(), utf16_input.size(), utf16_output.data());
9724
  } else
9725
    #endif
9726
  {
9727
    return change_endianness_utf16(utf16_input.data(), utf16_input.size(),
9728
                                   utf16_output.data());
9729
  }
9730
}
9731
  #endif // SIMDUTF_SPAN
9732
#endif   // SIMDUTF_FEATURE_UTF16
9733
9734
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
9735
/**
9736
 * Compute the number of bytes that this UTF-32 string would require in UTF-8
9737
 * format.
9738
 *
9739
 * This function does not validate the input. It is acceptable to pass invalid
9740
 * UTF-32 strings but in such cases the result is implementation defined.
9741
 *
9742
 * @param input         the UTF-32 string to convert
9743
 * @param length        the length of the string in 4-byte code units (char32_t)
9744
 * @return the number of bytes required to encode the UTF-32 string as UTF-8
9745
 */
9746
simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *input,
9747
                                                  size_t length) noexcept;
9748
  #if SIMDUTF_SPAN
9749
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9750
utf8_length_from_utf32(std::span<const char32_t> valid_utf32_input) noexcept {
9751
    #if SIMDUTF_CPLUSPLUS23
9752
  if consteval {
9753
    return scalar::utf32::utf8_length_from_utf32(valid_utf32_input.data(),
9754
                                                 valid_utf32_input.size());
9755
  } else
9756
    #endif
9757
  {
9758
    return utf8_length_from_utf32(valid_utf32_input.data(),
9759
                                  valid_utf32_input.size());
9760
  }
9761
}
9762
  #endif // SIMDUTF_SPAN
9763
#endif   // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
9764
9765
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9766
/**
9767
 * Compute the number of two-byte code units that this UTF-32 string would
9768
 * require in UTF-16 format.
9769
 *
9770
 * This function does not validate the input. It is acceptable to pass invalid
9771
 * UTF-32 strings but in such cases the result is implementation defined.
9772
 *
9773
 * @param input         the UTF-32 string to convert
9774
 * @param length        the length of the string in 4-byte code units (char32_t)
9775
 * @return the number of bytes required to encode the UTF-32 string as UTF-16
9776
 */
9777
simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *input,
9778
                                                   size_t length) noexcept;
9779
  #if SIMDUTF_SPAN
9780
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9781
utf16_length_from_utf32(std::span<const char32_t> valid_utf32_input) noexcept {
9782
    #if SIMDUTF_CPLUSPLUS23
9783
  if consteval {
9784
    return scalar::utf32::utf16_length_from_utf32(valid_utf32_input.data(),
9785
                                                  valid_utf32_input.size());
9786
  } else
9787
    #endif
9788
  {
9789
    return utf16_length_from_utf32(valid_utf32_input.data(),
9790
                                   valid_utf32_input.size());
9791
  }
9792
}
9793
  #endif // SIMDUTF_SPAN
9794
9795
/**
9796
 * Using native endianness; Compute the number of bytes that this UTF-16
9797
 * string would require in UTF-32 format.
9798
 *
9799
 * This function is equivalent to count_utf16.
9800
 *
9801
 * This function does not validate the input. It is acceptable to pass invalid
9802
 * UTF-16 strings but in such cases the result is implementation defined.
9803
 *
9804
 * This function is not BOM-aware.
9805
 *
9806
 * @param input         the UTF-16 string to convert
9807
 * @param length        the length of the string in 2-byte code units (char16_t)
9808
 * @return the number of bytes required to encode the UTF-16LE string as UTF-32
9809
 */
9810
simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t *input,
9811
                                                   size_t length) noexcept;
9812
  #if SIMDUTF_SPAN
9813
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9814
utf32_length_from_utf16(std::span<const char16_t> valid_utf16_input) noexcept {
9815
    #if SIMDUTF_CPLUSPLUS23
9816
  if consteval {
9817
    return scalar::utf16::utf32_length_from_utf16<endianness::NATIVE>(
9818
        valid_utf16_input.data(), valid_utf16_input.size());
9819
  } else
9820
    #endif
9821
  {
9822
    return utf32_length_from_utf16(valid_utf16_input.data(),
9823
                                   valid_utf16_input.size());
9824
  }
9825
}
9826
  #endif // SIMDUTF_SPAN
9827
9828
/**
9829
 * Compute the number of bytes that this UTF-16LE string would require in UTF-32
9830
 * format.
9831
 *
9832
 * This function is equivalent to count_utf16le.
9833
 *
9834
 * This function does not validate the input. It is acceptable to pass invalid
9835
 * UTF-16 strings but in such cases the result is implementation defined.
9836
 *
9837
 * This function is not BOM-aware.
9838
 *
9839
 * @param input         the UTF-16LE string to convert
9840
 * @param length        the length of the string in 2-byte code units (char16_t)
9841
 * @return the number of bytes required to encode the UTF-16LE string as UTF-32
9842
 */
9843
simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *input,
9844
                                                     size_t length) noexcept;
9845
  #if SIMDUTF_SPAN
9846
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9847
utf32_length_from_utf16le(
9848
    std::span<const char16_t> valid_utf16_input) noexcept {
9849
    #if SIMDUTF_CPLUSPLUS23
9850
  if consteval {
9851
    return scalar::utf16::utf32_length_from_utf16<endianness::LITTLE>(
9852
        valid_utf16_input.data(), valid_utf16_input.size());
9853
  } else
9854
    #endif
9855
  {
9856
    return utf32_length_from_utf16le(valid_utf16_input.data(),
9857
                                     valid_utf16_input.size());
9858
  }
9859
}
9860
  #endif // SIMDUTF_SPAN
9861
9862
/**
9863
 * Compute the number of bytes that this UTF-16BE string would require in UTF-32
9864
 * format.
9865
 *
9866
 * This function is equivalent to count_utf16be.
9867
 *
9868
 * This function does not validate the input. It is acceptable to pass invalid
9869
 * UTF-16 strings but in such cases the result is implementation defined.
9870
 *
9871
 * This function is not BOM-aware.
9872
 *
9873
 * @param input         the UTF-16BE string to convert
9874
 * @param length        the length of the string in 2-byte code units (char16_t)
9875
 * @return the number of bytes required to encode the UTF-16BE string as UTF-32
9876
 */
9877
simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *input,
9878
                                                     size_t length) noexcept;
9879
  #if SIMDUTF_SPAN
9880
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9881
utf32_length_from_utf16be(
9882
    std::span<const char16_t> valid_utf16_input) noexcept {
9883
    #if SIMDUTF_CPLUSPLUS23
9884
  if consteval {
9885
    return scalar::utf16::utf32_length_from_utf16<endianness::BIG>(
9886
        valid_utf16_input.data(), valid_utf16_input.size());
9887
  } else
9888
    #endif
9889
  {
9890
    return utf32_length_from_utf16be(valid_utf16_input.data(),
9891
                                     valid_utf16_input.size());
9892
  }
9893
}
9894
  #endif // SIMDUTF_SPAN
9895
#endif   // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
9896
9897
#if SIMDUTF_FEATURE_UTF16
9898
/**
9899
 * Count the number of code points (characters) in the string assuming that
9900
 * it is valid.
9901
 *
9902
 * This function assumes that the input string is valid UTF-16 (native
9903
 * endianness). It is acceptable to pass invalid UTF-16 strings but in such
9904
 * cases the result is implementation defined.
9905
 *
9906
 * This function is not BOM-aware.
9907
 *
9908
 * @param input         the UTF-16 string to process
9909
 * @param length        the length of the string in 2-byte code units (char16_t)
9910
 * @return number of code points
9911
 */
9912
simdutf_warn_unused size_t count_utf16(const char16_t *input,
9913
                                       size_t length) noexcept;
9914
  #if SIMDUTF_SPAN
9915
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9916
count_utf16(std::span<const char16_t> valid_utf16_input) noexcept {
9917
    #if SIMDUTF_CPLUSPLUS23
9918
  if consteval {
9919
    return scalar::utf16::count_code_points<endianness::NATIVE>(
9920
        valid_utf16_input.data(), valid_utf16_input.size());
9921
  } else
9922
    #endif
9923
  {
9924
    return count_utf16(valid_utf16_input.data(), valid_utf16_input.size());
9925
  }
9926
}
9927
  #endif // SIMDUTF_SPAN
9928
9929
/**
9930
 * Count the number of code points (characters) in the string assuming that
9931
 * it is valid.
9932
 *
9933
 * This function assumes that the input string is valid UTF-16LE.
9934
 * It is acceptable to pass invalid UTF-16 strings but in such cases
9935
 * the result is implementation defined.
9936
 *
9937
 * This function is not BOM-aware.
9938
 *
9939
 * @param input         the UTF-16LE string to process
9940
 * @param length        the length of the string in 2-byte code units (char16_t)
9941
 * @return number of code points
9942
 */
9943
simdutf_warn_unused size_t count_utf16le(const char16_t *input,
9944
                                         size_t length) noexcept;
9945
  #if SIMDUTF_SPAN
9946
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9947
count_utf16le(std::span<const char16_t> valid_utf16_input) noexcept {
9948
    #if SIMDUTF_CPLUSPLUS23
9949
  if consteval {
9950
    return scalar::utf16::count_code_points<endianness::LITTLE>(
9951
        valid_utf16_input.data(), valid_utf16_input.size());
9952
  } else
9953
    #endif
9954
  {
9955
    return count_utf16le(valid_utf16_input.data(), valid_utf16_input.size());
9956
  }
9957
}
9958
  #endif // SIMDUTF_SPAN
9959
9960
/**
9961
 * Count the number of code points (characters) in the string assuming that
9962
 * it is valid.
9963
 *
9964
 * This function assumes that the input string is valid UTF-16BE.
9965
 * It is acceptable to pass invalid UTF-16 strings but in such cases
9966
 * the result is implementation defined.
9967
 *
9968
 * This function is not BOM-aware.
9969
 *
9970
 * @param input         the UTF-16BE string to process
9971
 * @param length        the length of the string in 2-byte code units (char16_t)
9972
 * @return number of code points
9973
 */
9974
simdutf_warn_unused size_t count_utf16be(const char16_t *input,
9975
                                         size_t length) noexcept;
9976
  #if SIMDUTF_SPAN
9977
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
9978
count_utf16be(std::span<const char16_t> valid_utf16_input) noexcept {
9979
    #if SIMDUTF_CPLUSPLUS23
9980
  if consteval {
9981
    return scalar::utf16::count_code_points<endianness::BIG>(
9982
        valid_utf16_input.data(), valid_utf16_input.size());
9983
  } else
9984
    #endif
9985
  {
9986
    return count_utf16be(valid_utf16_input.data(), valid_utf16_input.size());
9987
  }
9988
}
9989
  #endif // SIMDUTF_SPAN
9990
#endif   // SIMDUTF_FEATURE_UTF16
9991
9992
#if SIMDUTF_FEATURE_UTF8
9993
/**
9994
 * Count the number of code points (characters) in the string assuming that
9995
 * it is valid.
9996
 *
9997
 * This function assumes that the input string is valid UTF-8.
9998
 * It is acceptable to pass invalid UTF-8 strings but in such cases
9999
 * the result is implementation defined.
10000
 *
10001
 * @param input         the UTF-8 string to process
10002
 * @param length        the length of the string in bytes
10003
 * @return number of code points
10004
 */
10005
simdutf_warn_unused size_t count_utf8(const char *input,
10006
                                      size_t length) noexcept;
10007
  #if SIMDUTF_SPAN
10008
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t count_utf8(
10009
    const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept {
10010
    #if SIMDUTF_CPLUSPLUS23
10011
  if consteval {
10012
    return scalar::utf8::count_code_points(valid_utf8_input.data(),
10013
                                           valid_utf8_input.size());
10014
  } else
10015
    #endif
10016
  {
10017
    return count_utf8(reinterpret_cast<const char *>(valid_utf8_input.data()),
10018
                      valid_utf8_input.size());
10019
  }
10020
}
10021
  #endif // SIMDUTF_SPAN
10022
10023
/**
10024
 * Given a valid UTF-8 string having a possibly truncated last character,
10025
 * this function checks the end of string. If the last character is truncated
10026
 * (or partial), then it returns a shorter length (shorter by 1 to 3 bytes) so
10027
 * that the short UTF-8 strings only contain complete characters. If there is no
10028
 * truncated character, the original length is returned.
10029
 *
10030
 * This function assumes that the input string is valid UTF-8, but possibly
10031
 * truncated.
10032
 *
10033
 * @param input         the UTF-8 string to process
10034
 * @param length        the length of the string in bytes
10035
 * @return the length of the string in bytes, possibly shorter by 1 to 3 bytes
10036
 */
10037
simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length);
10038
  #if SIMDUTF_SPAN
10039
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
10040
trim_partial_utf8(
10041
    const detail::input_span_of_byte_like auto &valid_utf8_input) noexcept {
10042
    #if SIMDUTF_CPLUSPLUS23
10043
  if consteval {
10044
    return scalar::utf8::trim_partial_utf8(valid_utf8_input.data(),
10045
                                           valid_utf8_input.size());
10046
  } else
10047
    #endif
10048
  {
10049
    return trim_partial_utf8(
10050
        reinterpret_cast<const char *>(valid_utf8_input.data()),
10051
        valid_utf8_input.size());
10052
  }
10053
}
10054
  #endif // SIMDUTF_SPAN
10055
#endif   // SIMDUTF_FEATURE_UTF8
10056
10057
#if SIMDUTF_FEATURE_UTF16
10058
/**
10059
 * Given a valid UTF-16BE string having a possibly truncated last character,
10060
 * this function checks the end of string. If the last character is truncated
10061
 * (or partial), then it returns a shorter length (shorter by 1 unit) so that
10062
 * the short UTF-16BE strings only contain complete characters. If there is no
10063
 * truncated character, the original length is returned.
10064
 *
10065
 * This function assumes that the input string is valid UTF-16BE, but possibly
10066
 * truncated.
10067
 *
10068
 * @param input         the UTF-16BE string to process
10069
 * @param length        the length of the string in bytes
10070
 * @return the length of the string in bytes, possibly shorter by 1 unit
10071
 */
10072
simdutf_warn_unused size_t trim_partial_utf16be(const char16_t *input,
10073
                                                size_t length);
10074
  #if SIMDUTF_SPAN
10075
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
10076
trim_partial_utf16be(std::span<const char16_t> valid_utf16_input) noexcept {
10077
    #if SIMDUTF_CPLUSPLUS23
10078
  if consteval {
10079
    return scalar::utf16::trim_partial_utf16<endianness::BIG>(
10080
        valid_utf16_input.data(), valid_utf16_input.size());
10081
  } else
10082
    #endif
10083
  {
10084
    return trim_partial_utf16be(valid_utf16_input.data(),
10085
                                valid_utf16_input.size());
10086
  }
10087
}
10088
  #endif // SIMDUTF_SPAN
10089
10090
/**
10091
 * Given a valid UTF-16LE string having a possibly truncated last character,
10092
 * this function checks the end of string. If the last character is truncated
10093
 * (or partial), then it returns a shorter length (shorter by 1 unit) so that
10094
 * the short UTF-16LE strings only contain complete characters. If there is no
10095
 * truncated character, the original length is returned.
10096
 *
10097
 * This function assumes that the input string is valid UTF-16LE, but possibly
10098
 * truncated.
10099
 *
10100
 * @param input         the UTF-16LE string to process
10101
 * @param length        the length of the string in bytes
10102
 * @return the length of the string in unit, possibly shorter by 1 unit
10103
 */
10104
simdutf_warn_unused size_t trim_partial_utf16le(const char16_t *input,
10105
                                                size_t length);
10106
  #if SIMDUTF_SPAN
10107
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
10108
trim_partial_utf16le(std::span<const char16_t> valid_utf16_input) noexcept {
10109
    #if SIMDUTF_CPLUSPLUS23
10110
  if consteval {
10111
    return scalar::utf16::trim_partial_utf16<endianness::LITTLE>(
10112
        valid_utf16_input.data(), valid_utf16_input.size());
10113
  } else
10114
    #endif
10115
  {
10116
    return trim_partial_utf16le(valid_utf16_input.data(),
10117
                                valid_utf16_input.size());
10118
  }
10119
}
10120
  #endif // SIMDUTF_SPAN
10121
10122
/**
10123
 * Given a valid UTF-16 string having a possibly truncated last character,
10124
 * this function checks the end of string. If the last character is truncated
10125
 * (or partial), then it returns a shorter length (shorter by 1 unit) so that
10126
 * the short UTF-16 strings only contain complete characters. If there is no
10127
 * truncated character, the original length is returned.
10128
 *
10129
 * This function assumes that the input string is valid UTF-16, but possibly
10130
 * truncated. We use the native endianness.
10131
 *
10132
 * @param input         the UTF-16 string to process
10133
 * @param length        the length of the string in bytes
10134
 * @return the length of the string in unit, possibly shorter by 1 unit
10135
 */
10136
simdutf_warn_unused size_t trim_partial_utf16(const char16_t *input,
10137
                                              size_t length);
10138
  #if SIMDUTF_SPAN
10139
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
10140
trim_partial_utf16(std::span<const char16_t> valid_utf16_input) noexcept {
10141
    #if SIMDUTF_CPLUSPLUS23
10142
  if consteval {
10143
    return scalar::utf16::trim_partial_utf16<endianness::NATIVE>(
10144
        valid_utf16_input.data(), valid_utf16_input.size());
10145
  } else
10146
    #endif
10147
  {
10148
    return trim_partial_utf16(valid_utf16_input.data(),
10149
                              valid_utf16_input.size());
10150
  }
10151
}
10152
  #endif // SIMDUTF_SPAN
10153
#endif   // SIMDUTF_FEATURE_UTF16
10154
10155
#if SIMDUTF_FEATURE_BASE64 || SIMDUTF_FEATURE_UTF16 ||                         \
10156
    SIMDUTF_FEATURE_DETECT_ENCODING
10157
  #ifndef SIMDUTF_NEED_TRAILING_ZEROES
10158
    #define SIMDUTF_NEED_TRAILING_ZEROES 1
10159
  #endif
10160
#endif // SIMDUTF_FEATURE_BASE64 || SIMDUTF_FEATURE_UTF16 ||
10161
       // SIMDUTF_FEATURE_DETECT_ENCODING
10162
10163
#if SIMDUTF_FEATURE_BASE64
10164
// base64_options are used to specify the base64 encoding options.
10165
// ASCII spaces are ' ', '\t', '\n', '\r', '\f'
10166
// garbage characters are characters that are not part of the base64 alphabet
10167
// nor ASCII spaces.
10168
constexpr uint64_t base64_reverse_padding =
10169
    2; /* modifier for base64_default and base64_url */
10170
enum base64_options : uint64_t {
10171
  base64_default = 0, /* standard base64 format (with padding) */
10172
  base64_url = 1,     /* base64url format (no padding) */
10173
  base64_default_no_padding =
10174
      base64_default |
10175
      base64_reverse_padding, /* standard base64 format without padding */
10176
  base64_url_with_padding =
10177
      base64_url | base64_reverse_padding, /* base64url with padding */
10178
  base64_default_accept_garbage =
10179
      4, /* standard base64 format accepting garbage characters, the input stops
10180
            with the first '=' if any */
10181
  base64_url_accept_garbage =
10182
      5, /* base64url format accepting garbage characters, the input stops with
10183
            the first '=' if any */
10184
  base64_default_or_url =
10185
      8, /* standard/base64url hybrid format (only meaningful for decoding!) */
10186
  base64_default_or_url_accept_garbage =
10187
      12, /* standard/base64url hybrid format accepting garbage characters
10188
             (only meaningful for decoding!), the input stops with the first '='
10189
             if any */
10190
};
10191
10192
// last_chunk_handling_options are used to specify the handling of the last
10193
// chunk in base64 decoding.
10194
// https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
10195
enum last_chunk_handling_options : uint64_t {
10196
  loose = 0,  /* standard base64 format, decode partial final chunk */
10197
  strict = 1, /* error when the last chunk is partial, 2 or 3 chars, and
10198
                 unpadded, or non-zero bit padding */
10199
  stop_before_partial =
10200
      2, /* if the last chunk is partial, ignore it (no error) */
10201
  only_full_chunks =
10202
      3 /* only decode full blocks (4 base64 characters, no padding) */
10203
};
10204
10205
inline simdutf_constexpr23 bool
10206
43.5k
is_partial(last_chunk_handling_options options) {
10207
43.5k
  return (options == stop_before_partial) || (options == only_full_chunks);
10208
43.5k
}
10209
10210
namespace detail {
10211
simdutf_warn_unused const char *find(const char *start, const char *end,
10212
                                     char character) noexcept;
10213
simdutf_warn_unused const char16_t *
10214
find(const char16_t *start, const char16_t *end, char16_t character) noexcept;
10215
} // namespace detail
10216
10217
/**
10218
 * Find the first occurrence of a character in a string. If the character is
10219
 * not found, return a pointer to the end of the string.
10220
 * @param start        the start of the string
10221
 * @param end          the end of the string
10222
 * @param character    the character to find
10223
 * @return a pointer to the first occurrence of the character in the string,
10224
 * or a pointer to the end of the string if the character is not found.
10225
 *
10226
 */
10227
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 const char *
10228
0
find(const char *start, const char *end, char character) noexcept {
10229
  #if SIMDUTF_CPLUSPLUS23
10230
  if consteval {
10231
    for (; start != end; ++start)
10232
      if (*start == character)
10233
        return start;
10234
    return end;
10235
  } else
10236
  #endif
10237
0
  {
10238
0
    return detail::find(start, end, character);
10239
0
  }
10240
0
}
10241
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 const char16_t *
10242
0
find(const char16_t *start, const char16_t *end, char16_t character) noexcept {
10243
    // implementation note: this is repeated instead of a template, to ensure
10244
    // the api is still a function and compiles without concepts
10245
  #if SIMDUTF_CPLUSPLUS23
10246
  if consteval {
10247
    for (; start != end; ++start)
10248
      if (*start == character)
10249
        return start;
10250
    return end;
10251
  } else
10252
  #endif
10253
0
  {
10254
0
    return detail::find(start, end, character);
10255
0
  }
10256
0
}
10257
}
10258
  // We include base64_tables once.
10259
/* begin file include/simdutf/base64_tables.h */
10260
#ifndef SIMDUTF_BASE64_TABLES_H
10261
#define SIMDUTF_BASE64_TABLES_H
10262
#include <cstdint>
10263
10264
namespace simdutf {
10265
namespace {
10266
namespace tables {
10267
namespace base64 {
10268
namespace base64_default {
10269
10270
constexpr char e0[256] = {
10271
    'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D',
10272
    'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H',
10273
    'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L',
10274
    'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O',
10275
    'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S',
10276
    'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W',
10277
    'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a',
10278
    'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd',
10279
    'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h',
10280
    'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l',
10281
    'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p',
10282
    'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's',
10283
    't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w',
10284
    'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0',
10285
    '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4',
10286
    '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7',
10287
    '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/',
10288
    '/'};
10289
10290
constexpr char e1[256] = {
10291
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
10292
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
10293
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
10294
    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
10295
    '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
10296
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
10297
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
10298
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
10299
    '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
10300
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
10301
    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
10302
    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
10303
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C',
10304
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
10305
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
10306
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
10307
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+',
10308
    '/'};
10309
10310
constexpr char e2[256] = {
10311
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
10312
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
10313
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
10314
    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
10315
    '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
10316
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
10317
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
10318
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
10319
    '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
10320
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
10321
    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
10322
    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
10323
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C',
10324
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
10325
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
10326
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
10327
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+',
10328
    '/'};
10329
10330
constexpr uint32_t d0[256] = {
10331
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10332
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10333
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10334
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10335
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10336
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10337
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10338
    0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
10339
    0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
10340
    0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
10341
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10342
    0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
10343
    0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
10344
    0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
10345
    0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
10346
    0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10347
    0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
10348
    0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
10349
    0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
10350
    0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
10351
    0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10352
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10353
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10354
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10355
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10356
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10357
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10358
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10359
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10360
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10361
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10362
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10363
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10364
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10365
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10366
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10367
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10368
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10369
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10370
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10371
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10372
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10373
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10374
10375
constexpr uint32_t d1[256] = {
10376
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10377
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10378
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10379
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10380
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10381
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10382
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10383
    0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
10384
    0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
10385
    0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
10386
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10387
    0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
10388
    0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
10389
    0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
10390
    0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
10391
    0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10392
    0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
10393
    0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
10394
    0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
10395
    0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
10396
    0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10397
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10398
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10399
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10400
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10401
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10402
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10403
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10404
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10405
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10406
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10407
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10408
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10409
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10410
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10411
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10412
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10413
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10414
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10415
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10416
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10417
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10418
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10419
10420
constexpr uint32_t d2[256] = {
10421
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10422
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10423
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10424
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10425
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10426
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10427
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10428
    0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
10429
    0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
10430
    0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
10431
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10432
    0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
10433
    0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
10434
    0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
10435
    0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
10436
    0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10437
    0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
10438
    0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
10439
    0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
10440
    0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
10441
    0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10442
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10443
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10444
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10445
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10446
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10447
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10448
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10449
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10450
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10451
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10452
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10453
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10454
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10455
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10456
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10457
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10458
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10459
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10460
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10461
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10462
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10463
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10464
10465
constexpr uint32_t d3[256] = {
10466
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10467
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10468
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10469
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10470
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10471
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10472
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10473
    0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
10474
    0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
10475
    0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
10476
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10477
    0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
10478
    0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
10479
    0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
10480
    0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
10481
    0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10482
    0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
10483
    0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
10484
    0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
10485
    0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
10486
    0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10487
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10488
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10489
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10490
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10491
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10492
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10493
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10494
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10495
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10496
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10497
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10498
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10499
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10500
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10501
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10502
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10503
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10504
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10505
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10506
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10507
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10508
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10509
} // namespace base64_default
10510
10511
namespace base64_url {
10512
10513
constexpr char e0[256] = {
10514
    'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D',
10515
    'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H',
10516
    'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L',
10517
    'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O',
10518
    'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S',
10519
    'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W',
10520
    'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a',
10521
    'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd',
10522
    'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h',
10523
    'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l',
10524
    'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p',
10525
    'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's',
10526
    't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w',
10527
    'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0',
10528
    '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4',
10529
    '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7',
10530
    '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', '-', '-', '_', '_', '_',
10531
    '_'};
10532
10533
constexpr char e1[256] = {
10534
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
10535
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
10536
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
10537
    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
10538
    '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
10539
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
10540
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
10541
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
10542
    '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
10543
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
10544
    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
10545
    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
10546
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C',
10547
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
10548
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
10549
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
10550
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-',
10551
    '_'};
10552
10553
constexpr char e2[256] = {
10554
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
10555
    'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
10556
    'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
10557
    't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
10558
    '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
10559
    'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
10560
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
10561
    'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
10562
    '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
10563
    'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
10564
    'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
10565
    'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
10566
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C',
10567
    'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
10568
    'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
10569
    'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
10570
    'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-',
10571
    '_'};
10572
10573
constexpr uint32_t d0[256] = {
10574
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10575
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10576
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10577
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10578
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10579
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10580
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10581
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff,
10582
    0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
10583
    0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
10584
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10585
    0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
10586
    0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
10587
    0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
10588
    0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
10589
    0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
10590
    0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
10591
    0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
10592
    0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
10593
    0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
10594
    0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10595
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10596
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10597
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10598
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10599
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10600
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10601
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10602
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10603
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10604
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10605
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10606
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10607
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10608
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10609
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10610
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10611
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10612
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10613
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10614
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10615
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10616
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10617
constexpr uint32_t d1[256] = {
10618
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10619
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10620
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10621
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10622
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10623
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10624
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10625
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff,
10626
    0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
10627
    0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
10628
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10629
    0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
10630
    0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
10631
    0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
10632
    0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
10633
    0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
10634
    0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
10635
    0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
10636
    0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
10637
    0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
10638
    0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10639
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10640
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10641
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10642
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10643
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10644
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10645
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10646
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10647
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10648
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10649
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10650
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10651
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10652
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10653
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10654
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10655
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10656
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10657
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10658
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10659
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10660
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10661
constexpr uint32_t d2[256] = {
10662
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10663
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10664
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10665
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10666
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10667
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10668
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10669
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff,
10670
    0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
10671
    0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
10672
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10673
    0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
10674
    0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
10675
    0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
10676
    0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
10677
    0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
10678
    0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
10679
    0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
10680
    0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
10681
    0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
10682
    0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10683
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10684
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10685
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10686
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10687
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10688
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10689
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10690
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10691
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10692
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10693
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10694
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10695
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10696
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10697
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10698
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10699
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10700
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10701
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10702
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10703
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10704
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10705
constexpr uint32_t d3[256] = {
10706
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10707
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10708
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10709
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10710
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10711
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10712
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10713
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff,
10714
    0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
10715
    0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
10716
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10717
    0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
10718
    0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
10719
    0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
10720
    0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
10721
    0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
10722
    0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
10723
    0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
10724
    0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
10725
    0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
10726
    0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10727
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10728
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10729
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10730
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10731
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10732
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10733
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10734
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10735
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10736
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10737
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10738
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10739
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10740
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10741
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10742
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10743
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10744
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10745
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10746
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10747
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10748
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10749
} // namespace base64_url
10750
10751
namespace base64_default_or_url {
10752
constexpr uint32_t d0[256] = {
10753
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10754
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10755
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10756
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10757
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10758
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10759
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10760
    0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x000000fc,
10761
    0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
10762
    0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
10763
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10764
    0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
10765
    0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
10766
    0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
10767
    0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
10768
    0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
10769
    0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
10770
    0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
10771
    0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
10772
    0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
10773
    0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10774
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10775
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10776
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10777
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10778
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10779
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10780
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10781
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10782
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10783
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10784
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10785
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10786
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10787
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10788
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10789
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10790
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10791
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10792
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10793
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10794
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10795
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10796
constexpr uint32_t d1[256] = {
10797
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10798
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10799
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10800
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10801
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10802
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10803
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10804
    0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x0000f003,
10805
    0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
10806
    0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
10807
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10808
    0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
10809
    0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
10810
    0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
10811
    0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
10812
    0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
10813
    0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
10814
    0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
10815
    0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
10816
    0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
10817
    0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10818
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10819
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10820
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10821
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10822
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10823
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10824
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10825
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10826
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10827
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10828
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10829
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10830
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10831
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10832
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10833
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10834
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10835
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10836
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10837
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10838
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10839
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10840
constexpr uint32_t d2[256] = {
10841
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10842
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10843
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10844
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10845
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10846
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10847
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10848
    0x01ffffff, 0x00800f00, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x00c00f00,
10849
    0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
10850
    0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
10851
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10852
    0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
10853
    0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
10854
    0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
10855
    0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
10856
    0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
10857
    0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
10858
    0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
10859
    0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
10860
    0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
10861
    0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10862
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10863
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10864
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10865
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10866
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10867
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10868
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10869
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10870
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10871
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10872
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10873
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10874
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10875
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10876
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10877
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10878
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10879
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10880
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10881
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10882
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10883
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10884
constexpr uint32_t d3[256] = {
10885
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10886
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10887
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10888
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10889
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10890
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10891
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10892
    0x01ffffff, 0x003e0000, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x003f0000,
10893
    0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
10894
    0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
10895
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
10896
    0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
10897
    0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
10898
    0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
10899
    0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
10900
    0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
10901
    0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
10902
    0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
10903
    0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
10904
    0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
10905
    0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10906
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10907
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10908
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10909
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10910
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10911
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10912
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10913
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10914
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10915
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10916
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10917
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10918
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10919
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10920
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10921
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10922
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10923
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10924
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10925
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10926
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
10927
    0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
10928
} // namespace base64_default_or_url
10929
constexpr uint64_t thintable_epi8[256] = {
10930
    0x0706050403020100, 0x0007060504030201, 0x0007060504030200,
10931
    0x0000070605040302, 0x0007060504030100, 0x0000070605040301,
10932
    0x0000070605040300, 0x0000000706050403, 0x0007060504020100,
10933
    0x0000070605040201, 0x0000070605040200, 0x0000000706050402,
10934
    0x0000070605040100, 0x0000000706050401, 0x0000000706050400,
10935
    0x0000000007060504, 0x0007060503020100, 0x0000070605030201,
10936
    0x0000070605030200, 0x0000000706050302, 0x0000070605030100,
10937
    0x0000000706050301, 0x0000000706050300, 0x0000000007060503,
10938
    0x0000070605020100, 0x0000000706050201, 0x0000000706050200,
10939
    0x0000000007060502, 0x0000000706050100, 0x0000000007060501,
10940
    0x0000000007060500, 0x0000000000070605, 0x0007060403020100,
10941
    0x0000070604030201, 0x0000070604030200, 0x0000000706040302,
10942
    0x0000070604030100, 0x0000000706040301, 0x0000000706040300,
10943
    0x0000000007060403, 0x0000070604020100, 0x0000000706040201,
10944
    0x0000000706040200, 0x0000000007060402, 0x0000000706040100,
10945
    0x0000000007060401, 0x0000000007060400, 0x0000000000070604,
10946
    0x0000070603020100, 0x0000000706030201, 0x0000000706030200,
10947
    0x0000000007060302, 0x0000000706030100, 0x0000000007060301,
10948
    0x0000000007060300, 0x0000000000070603, 0x0000000706020100,
10949
    0x0000000007060201, 0x0000000007060200, 0x0000000000070602,
10950
    0x0000000007060100, 0x0000000000070601, 0x0000000000070600,
10951
    0x0000000000000706, 0x0007050403020100, 0x0000070504030201,
10952
    0x0000070504030200, 0x0000000705040302, 0x0000070504030100,
10953
    0x0000000705040301, 0x0000000705040300, 0x0000000007050403,
10954
    0x0000070504020100, 0x0000000705040201, 0x0000000705040200,
10955
    0x0000000007050402, 0x0000000705040100, 0x0000000007050401,
10956
    0x0000000007050400, 0x0000000000070504, 0x0000070503020100,
10957
    0x0000000705030201, 0x0000000705030200, 0x0000000007050302,
10958
    0x0000000705030100, 0x0000000007050301, 0x0000000007050300,
10959
    0x0000000000070503, 0x0000000705020100, 0x0000000007050201,
10960
    0x0000000007050200, 0x0000000000070502, 0x0000000007050100,
10961
    0x0000000000070501, 0x0000000000070500, 0x0000000000000705,
10962
    0x0000070403020100, 0x0000000704030201, 0x0000000704030200,
10963
    0x0000000007040302, 0x0000000704030100, 0x0000000007040301,
10964
    0x0000000007040300, 0x0000000000070403, 0x0000000704020100,
10965
    0x0000000007040201, 0x0000000007040200, 0x0000000000070402,
10966
    0x0000000007040100, 0x0000000000070401, 0x0000000000070400,
10967
    0x0000000000000704, 0x0000000703020100, 0x0000000007030201,
10968
    0x0000000007030200, 0x0000000000070302, 0x0000000007030100,
10969
    0x0000000000070301, 0x0000000000070300, 0x0000000000000703,
10970
    0x0000000007020100, 0x0000000000070201, 0x0000000000070200,
10971
    0x0000000000000702, 0x0000000000070100, 0x0000000000000701,
10972
    0x0000000000000700, 0x0000000000000007, 0x0006050403020100,
10973
    0x0000060504030201, 0x0000060504030200, 0x0000000605040302,
10974
    0x0000060504030100, 0x0000000605040301, 0x0000000605040300,
10975
    0x0000000006050403, 0x0000060504020100, 0x0000000605040201,
10976
    0x0000000605040200, 0x0000000006050402, 0x0000000605040100,
10977
    0x0000000006050401, 0x0000000006050400, 0x0000000000060504,
10978
    0x0000060503020100, 0x0000000605030201, 0x0000000605030200,
10979
    0x0000000006050302, 0x0000000605030100, 0x0000000006050301,
10980
    0x0000000006050300, 0x0000000000060503, 0x0000000605020100,
10981
    0x0000000006050201, 0x0000000006050200, 0x0000000000060502,
10982
    0x0000000006050100, 0x0000000000060501, 0x0000000000060500,
10983
    0x0000000000000605, 0x0000060403020100, 0x0000000604030201,
10984
    0x0000000604030200, 0x0000000006040302, 0x0000000604030100,
10985
    0x0000000006040301, 0x0000000006040300, 0x0000000000060403,
10986
    0x0000000604020100, 0x0000000006040201, 0x0000000006040200,
10987
    0x0000000000060402, 0x0000000006040100, 0x0000000000060401,
10988
    0x0000000000060400, 0x0000000000000604, 0x0000000603020100,
10989
    0x0000000006030201, 0x0000000006030200, 0x0000000000060302,
10990
    0x0000000006030100, 0x0000000000060301, 0x0000000000060300,
10991
    0x0000000000000603, 0x0000000006020100, 0x0000000000060201,
10992
    0x0000000000060200, 0x0000000000000602, 0x0000000000060100,
10993
    0x0000000000000601, 0x0000000000000600, 0x0000000000000006,
10994
    0x0000050403020100, 0x0000000504030201, 0x0000000504030200,
10995
    0x0000000005040302, 0x0000000504030100, 0x0000000005040301,
10996
    0x0000000005040300, 0x0000000000050403, 0x0000000504020100,
10997
    0x0000000005040201, 0x0000000005040200, 0x0000000000050402,
10998
    0x0000000005040100, 0x0000000000050401, 0x0000000000050400,
10999
    0x0000000000000504, 0x0000000503020100, 0x0000000005030201,
11000
    0x0000000005030200, 0x0000000000050302, 0x0000000005030100,
11001
    0x0000000000050301, 0x0000000000050300, 0x0000000000000503,
11002
    0x0000000005020100, 0x0000000000050201, 0x0000000000050200,
11003
    0x0000000000000502, 0x0000000000050100, 0x0000000000000501,
11004
    0x0000000000000500, 0x0000000000000005, 0x0000000403020100,
11005
    0x0000000004030201, 0x0000000004030200, 0x0000000000040302,
11006
    0x0000000004030100, 0x0000000000040301, 0x0000000000040300,
11007
    0x0000000000000403, 0x0000000004020100, 0x0000000000040201,
11008
    0x0000000000040200, 0x0000000000000402, 0x0000000000040100,
11009
    0x0000000000000401, 0x0000000000000400, 0x0000000000000004,
11010
    0x0000000003020100, 0x0000000000030201, 0x0000000000030200,
11011
    0x0000000000000302, 0x0000000000030100, 0x0000000000000301,
11012
    0x0000000000000300, 0x0000000000000003, 0x0000000000020100,
11013
    0x0000000000000201, 0x0000000000000200, 0x0000000000000002,
11014
    0x0000000000000100, 0x0000000000000001, 0x0000000000000000,
11015
    0x0000000000000000,
11016
};
11017
11018
constexpr uint8_t pshufb_combine_table[272] = {
11019
    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
11020
    0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
11021
    0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03,
11022
    0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff,
11023
    0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
11024
    0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b,
11025
    0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08,
11026
    0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
11027
    0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff,
11028
    0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
11029
    0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b,
11030
    0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
11031
};
11032
11033
constexpr unsigned char BitsSetTable256mul2[256] = {
11034
    0,  2,  2,  4,  2,  4,  4,  6,  2,  4,  4,  6,  4,  6,  6,  8,  2,  4,  4,
11035
    6,  4,  6,  6,  8,  4,  6,  6,  8,  6,  8,  8,  10, 2,  4,  4,  6,  4,  6,
11036
    6,  8,  4,  6,  6,  8,  6,  8,  8,  10, 4,  6,  6,  8,  6,  8,  8,  10, 6,
11037
    8,  8,  10, 8,  10, 10, 12, 2,  4,  4,  6,  4,  6,  6,  8,  4,  6,  6,  8,
11038
    6,  8,  8,  10, 4,  6,  6,  8,  6,  8,  8,  10, 6,  8,  8,  10, 8,  10, 10,
11039
    12, 4,  6,  6,  8,  6,  8,  8,  10, 6,  8,  8,  10, 8,  10, 10, 12, 6,  8,
11040
    8,  10, 8,  10, 10, 12, 8,  10, 10, 12, 10, 12, 12, 14, 2,  4,  4,  6,  4,
11041
    6,  6,  8,  4,  6,  6,  8,  6,  8,  8,  10, 4,  6,  6,  8,  6,  8,  8,  10,
11042
    6,  8,  8,  10, 8,  10, 10, 12, 4,  6,  6,  8,  6,  8,  8,  10, 6,  8,  8,
11043
    10, 8,  10, 10, 12, 6,  8,  8,  10, 8,  10, 10, 12, 8,  10, 10, 12, 10, 12,
11044
    12, 14, 4,  6,  6,  8,  6,  8,  8,  10, 6,  8,  8,  10, 8,  10, 10, 12, 6,
11045
    8,  8,  10, 8,  10, 10, 12, 8,  10, 10, 12, 10, 12, 12, 14, 6,  8,  8,  10,
11046
    8,  10, 10, 12, 8,  10, 10, 12, 10, 12, 12, 14, 8,  10, 10, 12, 10, 12, 12,
11047
    14, 10, 12, 12, 14, 12, 14, 14, 16};
11048
11049
constexpr uint8_t to_base64_value[] = {
11050
    255, 255, 255, 255, 255, 255, 255, 255, 255, 64,  64,  255, 64,  64,  255,
11051
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11052
    255, 255, 64,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
11053
    255, 255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
11054
    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
11055
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
11056
    25,  255, 255, 255, 255, 255, 255, 26,  27,  28,  29,  30,  31,  32,  33,
11057
    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
11058
    49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11059
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11060
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11061
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11062
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11063
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11064
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11065
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11066
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11067
    255};
11068
11069
constexpr uint8_t to_base64_url_value[] = {
11070
    255, 255, 255, 255, 255, 255, 255, 255, 255, 64,  64,  255, 64,  64,  255,
11071
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11072
    255, 255, 64,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11073
    62,  255, 255, 52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
11074
    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
11075
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
11076
    25,  255, 255, 255, 255, 63,  255, 26,  27,  28,  29,  30,  31,  32,  33,
11077
    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
11078
    49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11079
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11080
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11081
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11082
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11083
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11084
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11085
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11086
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11087
    255};
11088
11089
constexpr uint8_t to_base64_default_or_url_value[] = {
11090
    255, 255, 255, 255, 255, 255, 255, 255, 255, 64,  64,  255, 64,  64,  255,
11091
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11092
    255, 255, 64,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62,  255,
11093
    62,  255, 63,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  255, 255,
11094
    255, 255, 255, 255, 255, 0,   1,   2,   3,   4,   5,   6,   7,   8,   9,
11095
    10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
11096
    25,  255, 255, 255, 255, 63,  255, 26,  27,  28,  29,  30,  31,  32,  33,
11097
    34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,
11098
    49,  50,  51,  255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11099
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11100
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11101
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11102
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11103
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11104
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11105
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11106
    255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
11107
    255};
11108
11109
static_assert(sizeof(to_base64_value) == 256,
11110
              "to_base64_value must have 256 elements");
11111
static_assert(sizeof(to_base64_url_value) == 256,
11112
              "to_base64_url_value must have 256 elements");
11113
static_assert(to_base64_value[uint8_t(' ')] == 64,
11114
              "space must be == 64 in to_base64_value");
11115
static_assert(to_base64_url_value[uint8_t(' ')] == 64,
11116
              "space must be == 64 in to_base64_url_value");
11117
static_assert(to_base64_value[uint8_t('\t')] == 64,
11118
              "tab must be == 64 in to_base64_value");
11119
static_assert(to_base64_url_value[uint8_t('\t')] == 64,
11120
              "tab must be == 64 in to_base64_url_value");
11121
static_assert(to_base64_value[uint8_t('\r')] == 64,
11122
              "cr must be == 64 in to_base64_value");
11123
static_assert(to_base64_url_value[uint8_t('\r')] == 64,
11124
              "cr must be == 64 in to_base64_url_value");
11125
static_assert(to_base64_value[uint8_t('\n')] == 64,
11126
              "lf must be == 64 in to_base64_value");
11127
static_assert(to_base64_url_value[uint8_t('\n')] == 64,
11128
              "lf must be == 64 in to_base64_url_value");
11129
static_assert(to_base64_value[uint8_t('\f')] == 64,
11130
              "ff must be == 64 in to_base64_value");
11131
static_assert(to_base64_url_value[uint8_t('\f')] == 64,
11132
              "ff must be == 64 in to_base64_url_value");
11133
static_assert(to_base64_value[uint8_t('+')] == 62,
11134
              "+ must be == 62 in to_base64_value");
11135
static_assert(to_base64_url_value[uint8_t('-')] == 62,
11136
              "- must be == 62 in to_base64_url_value");
11137
static_assert(to_base64_value[uint8_t('/')] == 63,
11138
              "/ must be == 63 in to_base64_value");
11139
static_assert(to_base64_url_value[uint8_t('_')] == 63,
11140
              "_ must be == 63 in to_base64_url_value");
11141
} // namespace base64
11142
} // namespace tables
11143
} // unnamed namespace
11144
} // namespace simdutf
11145
11146
#endif // SIMDUTF_BASE64_TABLES_H
11147
/* end file include/simdutf/base64_tables.h */
11148
/* begin file include/simdutf/scalar/base64.h */
11149
#ifndef SIMDUTF_BASE64_H
11150
#define SIMDUTF_BASE64_H
11151
11152
#include <cstddef>
11153
#include <cstdint>
11154
#include <cstring>
11155
11156
namespace simdutf {
11157
namespace scalar {
11158
namespace {
11159
namespace base64 {
11160
11161
// This function is not expected to be fast. Do not use in long loops.
11162
// In most instances you should be using is_ignorable.
11163
template <class char_type> bool is_ascii_white_space(char_type c) {
11164
  return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
11165
}
11166
11167
344k
template <class char_type> simdutf_constexpr23 bool is_eight_byte(char_type c) {
11168
344k
  if constexpr (sizeof(char_type) == 1) {
11169
344k
    return true;
11170
344k
  }
11171
0
  return uint8_t(c) == c;
11172
344k
}
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char>(char)
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char16_t>(char16_t)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char>(char)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char16_t>(char16_t)
simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char>(char)
Line
Count
Source
11167
344k
template <class char_type> simdutf_constexpr23 bool is_eight_byte(char_type c) {
11168
344k
  if constexpr (sizeof(char_type) == 1) {
11169
344k
    return true;
11170
344k
  }
11171
0
  return uint8_t(c) == c;
11172
344k
}
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_eight_byte<char16_t>(char16_t)
11173
11174
template <class char_type>
11175
simdutf_constexpr23 bool is_ignorable(char_type c,
11176
0
                                      simdutf::base64_options options) {
11177
0
  const uint8_t *to_base64 =
11178
0
      (options & base64_default_or_url)
11179
0
          ? tables::base64::to_base64_default_or_url_value
11180
0
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11181
0
                                    : tables::base64::to_base64_value);
11182
0
  const bool ignore_garbage =
11183
0
      (options == base64_options::base64_url_accept_garbage) ||
11184
0
      (options == base64_options::base64_default_accept_garbage) ||
11185
0
      (options == base64_options::base64_default_or_url_accept_garbage);
11186
0
  uint8_t code = to_base64[uint8_t(c)];
11187
0
  if (is_eight_byte(c) && code <= 63) {
11188
0
    return false;
11189
0
  }
11190
0
  if (is_eight_byte(c) && code == 64) {
11191
0
    return true;
11192
0
  }
11193
0
  return ignore_garbage;
11194
0
}
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char>(char, simdutf::base64_options)
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char>(char, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char>(char, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_ignorable<char16_t>(char16_t, simdutf::base64_options)
11195
template <class char_type>
11196
simdutf_constexpr23 bool is_base64(char_type c,
11197
0
                                   simdutf::base64_options options) {
11198
0
  const uint8_t *to_base64 =
11199
0
      (options & base64_default_or_url)
11200
0
          ? tables::base64::to_base64_default_or_url_value
11201
0
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11202
0
                                    : tables::base64::to_base64_value);
11203
0
  uint8_t code = to_base64[uint8_t(c)];
11204
0
  if (is_eight_byte(c) && code <= 63) {
11205
0
    return true;
11206
0
  }
11207
0
  return false;
11208
0
}
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char>(char, simdutf::base64_options)
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char>(char, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char>(char, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64<char16_t>(char16_t, simdutf::base64_options)
11209
11210
template <class char_type>
11211
simdutf_constexpr23 bool is_base64_or_padding(char_type c,
11212
0
                                              simdutf::base64_options options) {
11213
0
  const uint8_t *to_base64 =
11214
0
      (options & base64_default_or_url)
11215
0
          ? tables::base64::to_base64_default_or_url_value
11216
0
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11217
0
                                    : tables::base64::to_base64_value);
11218
0
  if (c == '=') {
11219
0
    return true;
11220
0
  }
11221
0
  uint8_t code = to_base64[uint8_t(c)];
11222
0
  if (is_eight_byte(c) && code <= 63) {
11223
0
    return true;
11224
0
  }
11225
0
  return false;
11226
0
}
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char>(char, simdutf::base64_options)
Unexecuted instantiation: parser.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char>(char, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char16_t>(char16_t, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char>(char, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:bool simdutf::scalar::(anonymous namespace)::base64::is_base64_or_padding<char16_t>(char16_t, simdutf::base64_options)
11227
11228
template <class char_type>
11229
bool is_ignorable_or_padding(char_type c, simdutf::base64_options options) {
11230
  return is_ignorable(c, options) || c == '=';
11231
}
11232
11233
struct reduced_input {
11234
  size_t equalsigns;    // number of padding characters '=', typically 0, 1, 2.
11235
  size_t equallocation; // location of the first padding character if any
11236
  size_t srclen;        // length of the input buffer before padding
11237
  size_t full_input_length; // length of the input buffer with padding but
11238
                            // without ignorable characters
11239
};
11240
11241
// find the end of the base64 input buffer
11242
// It returns the number of padding characters, the location of the first
11243
// padding character if any, the length of the input buffer before padding
11244
// and the length of the input buffer with padding. The input buffer is not
11245
// modified. The function assumes that there are at most two padding characters.
11246
template <class char_type>
11247
simdutf_constexpr23 reduced_input find_end(const char_type *src, size_t srclen,
11248
22.7k
                                           simdutf::base64_options options) {
11249
22.7k
  const uint8_t *to_base64 =
11250
22.7k
      (options & base64_default_or_url)
11251
22.7k
          ? tables::base64::to_base64_default_or_url_value
11252
22.7k
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11253
22.7k
                                    : tables::base64::to_base64_value);
11254
22.7k
  const bool ignore_garbage =
11255
22.7k
      (options == base64_options::base64_url_accept_garbage) ||
11256
22.7k
      (options == base64_options::base64_default_accept_garbage) ||
11257
22.7k
      (options == base64_options::base64_default_or_url_accept_garbage);
11258
11259
22.7k
  size_t equalsigns = 0;
11260
  // We intentionally include trailing spaces in the full input length.
11261
  // See https://github.com/simdutf/simdutf/issues/824
11262
22.7k
  size_t full_input_length = srclen;
11263
  // skip trailing spaces
11264
25.0k
  while (!ignore_garbage && srclen > 0 &&
11265
25.0k
         scalar::base64::is_eight_byte(src[srclen - 1]) &&
11266
25.0k
         to_base64[uint8_t(src[srclen - 1])] == 64) {
11267
2.31k
    srclen--;
11268
2.31k
  }
11269
22.7k
  size_t equallocation =
11270
22.7k
      srclen; // location of the first padding character if any
11271
22.7k
  if (ignore_garbage) {
11272
    // Technically, we don't need to find the first padding character, we can
11273
    // just change our algorithms, but it adds substantial complexity.
11274
0
    auto it = simdutf::find(src, src + srclen, '=');
11275
0
    if (it != src + srclen) {
11276
0
      equallocation = it - src;
11277
0
      equalsigns = 1;
11278
0
      srclen = equallocation;
11279
0
      full_input_length = equallocation + 1;
11280
0
    }
11281
0
    return {equalsigns, equallocation, srclen, full_input_length};
11282
0
  }
11283
22.7k
  if (!ignore_garbage && srclen > 0 && src[srclen - 1] == '=') {
11284
    // This is the last '=' sign.
11285
2.56k
    equallocation = srclen - 1;
11286
2.56k
    srclen--;
11287
2.56k
    equalsigns = 1;
11288
    // skip trailing spaces
11289
3.00k
    while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
11290
3.00k
           to_base64[uint8_t(src[srclen - 1])] == 64) {
11291
440
      srclen--;
11292
440
    }
11293
2.56k
    if (srclen > 0 && src[srclen - 1] == '=') {
11294
      // This is the second '=' sign.
11295
283
      equallocation = srclen - 1;
11296
283
      srclen--;
11297
283
      equalsigns = 2;
11298
283
    }
11299
2.56k
  }
11300
22.7k
  return {equalsigns, equallocation, srclen, full_input_length};
11301
22.7k
}
simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::reduced_input simdutf::scalar::(anonymous namespace)::base64::find_end<char>(char const*, unsigned long, simdutf::base64_options)
Line
Count
Source
11248
22.7k
                                           simdutf::base64_options options) {
11249
22.7k
  const uint8_t *to_base64 =
11250
22.7k
      (options & base64_default_or_url)
11251
22.7k
          ? tables::base64::to_base64_default_or_url_value
11252
22.7k
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11253
22.7k
                                    : tables::base64::to_base64_value);
11254
22.7k
  const bool ignore_garbage =
11255
22.7k
      (options == base64_options::base64_url_accept_garbage) ||
11256
22.7k
      (options == base64_options::base64_default_accept_garbage) ||
11257
22.7k
      (options == base64_options::base64_default_or_url_accept_garbage);
11258
11259
22.7k
  size_t equalsigns = 0;
11260
  // We intentionally include trailing spaces in the full input length.
11261
  // See https://github.com/simdutf/simdutf/issues/824
11262
22.7k
  size_t full_input_length = srclen;
11263
  // skip trailing spaces
11264
25.0k
  while (!ignore_garbage && srclen > 0 &&
11265
25.0k
         scalar::base64::is_eight_byte(src[srclen - 1]) &&
11266
25.0k
         to_base64[uint8_t(src[srclen - 1])] == 64) {
11267
2.31k
    srclen--;
11268
2.31k
  }
11269
22.7k
  size_t equallocation =
11270
22.7k
      srclen; // location of the first padding character if any
11271
22.7k
  if (ignore_garbage) {
11272
    // Technically, we don't need to find the first padding character, we can
11273
    // just change our algorithms, but it adds substantial complexity.
11274
0
    auto it = simdutf::find(src, src + srclen, '=');
11275
0
    if (it != src + srclen) {
11276
0
      equallocation = it - src;
11277
0
      equalsigns = 1;
11278
0
      srclen = equallocation;
11279
0
      full_input_length = equallocation + 1;
11280
0
    }
11281
0
    return {equalsigns, equallocation, srclen, full_input_length};
11282
0
  }
11283
22.7k
  if (!ignore_garbage && srclen > 0 && src[srclen - 1] == '=') {
11284
    // This is the last '=' sign.
11285
2.56k
    equallocation = srclen - 1;
11286
2.56k
    srclen--;
11287
2.56k
    equalsigns = 1;
11288
    // skip trailing spaces
11289
3.00k
    while (srclen > 0 && scalar::base64::is_eight_byte(src[srclen - 1]) &&
11290
3.00k
           to_base64[uint8_t(src[srclen - 1])] == 64) {
11291
440
      srclen--;
11292
440
    }
11293
2.56k
    if (srclen > 0 && src[srclen - 1] == '=') {
11294
      // This is the second '=' sign.
11295
283
      equallocation = srclen - 1;
11296
283
      srclen--;
11297
283
      equalsigns = 2;
11298
283
    }
11299
2.56k
  }
11300
22.7k
  return {equalsigns, equallocation, srclen, full_input_length};
11301
22.7k
}
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::reduced_input simdutf::scalar::(anonymous namespace)::base64::find_end<char16_t>(char16_t const*, unsigned long, simdutf::base64_options)
11302
11303
// Returns true upon success. The destination buffer must be large enough.
11304
// This functions assumes that the padding (=) has been removed.
11305
// if check_capacity is true, it will check that the destination buffer is
11306
// large enough. If it is not, it will return OUTPUT_BUFFER_TOO_SMALL.
11307
template <bool check_capacity, class char_type>
11308
simdutf_constexpr23 full_result base64_tail_decode_impl(
11309
    char *dst, size_t outlen, const char_type *src, size_t length,
11310
    size_t padding_characters, // number of padding characters
11311
                               // '=', typically 0, 1, 2.
11312
21.8k
    base64_options options, last_chunk_handling_options last_chunk_options) {
11313
21.8k
  char *dstend = dst + outlen;
11314
21.8k
  (void)dstend;
11315
  // This looks like 10 branches, but we expect the compiler to resolve this to
11316
  // two branches (easily predicted):
11317
21.8k
  const uint8_t *to_base64 =
11318
21.8k
      (options & base64_default_or_url)
11319
21.8k
          ? tables::base64::to_base64_default_or_url_value
11320
21.8k
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11321
21.8k
                                    : tables::base64::to_base64_value);
11322
21.8k
  const uint32_t *d0 =
11323
21.8k
      (options & base64_default_or_url)
11324
21.8k
          ? tables::base64::base64_default_or_url::d0
11325
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d0
11326
21.8k
                                    : tables::base64::base64_default::d0);
11327
21.8k
  const uint32_t *d1 =
11328
21.8k
      (options & base64_default_or_url)
11329
21.8k
          ? tables::base64::base64_default_or_url::d1
11330
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d1
11331
21.8k
                                    : tables::base64::base64_default::d1);
11332
21.8k
  const uint32_t *d2 =
11333
21.8k
      (options & base64_default_or_url)
11334
21.8k
          ? tables::base64::base64_default_or_url::d2
11335
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d2
11336
21.8k
                                    : tables::base64::base64_default::d2);
11337
21.8k
  const uint32_t *d3 =
11338
21.8k
      (options & base64_default_or_url)
11339
21.8k
          ? tables::base64::base64_default_or_url::d3
11340
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d3
11341
21.8k
                                    : tables::base64::base64_default::d3);
11342
21.8k
  const bool ignore_garbage =
11343
21.8k
      (options == base64_options::base64_url_accept_garbage) ||
11344
21.8k
      (options == base64_options::base64_default_accept_garbage) ||
11345
21.8k
      (options == base64_options::base64_default_or_url_accept_garbage);
11346
11347
21.8k
  const char_type *srcend = src + length;
11348
21.8k
  const char_type *srcinit = src;
11349
21.8k
  const char *dstinit = dst;
11350
11351
21.8k
  uint32_t x;
11352
21.8k
  size_t idx;
11353
21.8k
  uint8_t buffer[4];
11354
21.9k
  while (true) {
11355
97.2k
    while (srcend - src >= 4 && is_eight_byte(src[0]) &&
11356
75.5k
           is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
11357
75.5k
           is_eight_byte(src[3]) &&
11358
75.5k
           (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
11359
75.5k
                d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
11360
75.3k
      if (check_capacity && dstend - dst < 3) {
11361
0
        return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
11362
0
                size_t(dst - dstinit)};
11363
0
      }
11364
75.3k
      *dst++ = static_cast<char>(x & 0xFF);
11365
75.3k
      *dst++ = static_cast<char>((x >> 8) & 0xFF);
11366
75.3k
      *dst++ = static_cast<char>((x >> 16) & 0xFF);
11367
75.3k
      src += 4;
11368
75.3k
    }
11369
21.9k
    const char_type *srccur = src;
11370
21.9k
    idx = 0;
11371
    // we need at least four characters.
11372
21.9k
#ifdef __clang__
11373
    // If possible, we read four characters at a time. (It is an optimization.)
11374
21.9k
    if (ignore_garbage && src + 4 <= srcend) {
11375
0
      char_type c0 = src[0];
11376
0
      char_type c1 = src[1];
11377
0
      char_type c2 = src[2];
11378
0
      char_type c3 = src[3];
11379
11380
0
      uint8_t code0 = to_base64[uint8_t(c0)];
11381
0
      uint8_t code1 = to_base64[uint8_t(c1)];
11382
0
      uint8_t code2 = to_base64[uint8_t(c2)];
11383
0
      uint8_t code3 = to_base64[uint8_t(c3)];
11384
11385
0
      buffer[idx] = code0;
11386
0
      idx += (is_eight_byte(c0) && code0 <= 63);
11387
0
      buffer[idx] = code1;
11388
0
      idx += (is_eight_byte(c1) && code1 <= 63);
11389
0
      buffer[idx] = code2;
11390
0
      idx += (is_eight_byte(c2) && code2 <= 63);
11391
0
      buffer[idx] = code3;
11392
0
      idx += (is_eight_byte(c3) && code3 <= 63);
11393
0
      src += 4;
11394
0
    }
11395
21.9k
#endif
11396
32.5k
    while ((idx < 4) && (src < srcend)) {
11397
10.6k
      char_type c = *src;
11398
11399
10.6k
      uint8_t code = to_base64[uint8_t(c)];
11400
10.6k
      buffer[idx] = uint8_t(code);
11401
10.6k
      if (is_eight_byte(c) && code <= 63) {
11402
7.85k
        idx++;
11403
7.85k
      } else if (!ignore_garbage &&
11404
2.83k
                 (code > 64 || !scalar::base64::is_eight_byte(c))) {
11405
101
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11406
101
                size_t(dst - dstinit)};
11407
2.73k
      } else {
11408
        // We have a space or a newline or garbage. We ignore it.
11409
2.73k
      }
11410
10.5k
      src++;
11411
10.5k
    }
11412
21.8k
    if (idx != 4) {
11413
21.7k
      simdutf_log_assert(idx < 4, "idx should be less than 4");
11414
      // We never should have that the number of base64 characters + the
11415
      // number of padding characters is more than 4.
11416
21.7k
      if (!ignore_garbage && (idx + padding_characters > 4)) {
11417
1
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11418
1
                size_t(dst - dstinit), true};
11419
1
      }
11420
11421
      // The idea here is that in loose mode,
11422
      // if there is padding at all, it must be used
11423
      // to form 4-wise chunk. However, in loose mode,
11424
      // we do accept no padding at all.
11425
21.7k
      if (!ignore_garbage &&
11426
21.7k
          last_chunk_options == last_chunk_handling_options::loose &&
11427
21.7k
          (idx >= 2) && padding_characters > 0 &&
11428
2.46k
          ((idx + padding_characters) & 3) != 0) {
11429
12
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11430
12
                size_t(dst - dstinit), true};
11431
12
      } else
11432
11433
        // The idea here is that in strict mode, we do not want to accept
11434
        // incomplete base64 chunks. So if the chunk was otherwise valid, we
11435
        // return BASE64_INPUT_REMAINDER.
11436
21.7k
        if (!ignore_garbage &&
11437
21.7k
            last_chunk_options == last_chunk_handling_options::strict &&
11438
0
            (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
11439
          // The partial chunk was at src - idx
11440
0
          return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
11441
0
                  size_t(dst - dstinit), true};
11442
0
        } else
11443
          // If there is a partial chunk with insufficient padding, with
11444
          // stop_before_partial, we need to just ignore it. In "only full"
11445
          // mode, skip the minute there are padding characters.
11446
21.7k
          if ((last_chunk_options ==
11447
21.7k
                   last_chunk_handling_options::stop_before_partial &&
11448
0
               (padding_characters + idx < 4) && (idx != 0) &&
11449
0
               (idx >= 2 || padding_characters == 0)) ||
11450
21.7k
              (last_chunk_options ==
11451
21.7k
                   last_chunk_handling_options::only_full_chunks &&
11452
0
               (idx >= 2 || padding_characters == 0))) {
11453
            // partial means that we are *not* going to consume the read
11454
            // characters. We need to rewind the src pointer.
11455
0
            src = srccur;
11456
0
            return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
11457
21.7k
          } else {
11458
21.7k
            if (idx == 2) {
11459
308
              uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
11460
308
                                (uint32_t(buffer[1]) << 2 * 6);
11461
308
              if (!ignore_garbage &&
11462
308
                  (last_chunk_options == last_chunk_handling_options::strict) &&
11463
0
                  (triple & 0xffff)) {
11464
0
                return {BASE64_EXTRA_BITS, size_t(src - srcinit),
11465
0
                        size_t(dst - dstinit)};
11466
0
              }
11467
308
              if (check_capacity && dstend - dst < 1) {
11468
0
                return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11469
0
                        size_t(dst - dstinit)};
11470
0
              }
11471
308
              *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11472
21.4k
            } else if (idx == 3) {
11473
2.20k
              uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
11474
2.20k
                                (uint32_t(buffer[1]) << 2 * 6) +
11475
2.20k
                                (uint32_t(buffer[2]) << 1 * 6);
11476
2.20k
              if (!ignore_garbage &&
11477
2.20k
                  (last_chunk_options == last_chunk_handling_options::strict) &&
11478
0
                  (triple & 0xff)) {
11479
0
                return {BASE64_EXTRA_BITS, size_t(src - srcinit),
11480
0
                        size_t(dst - dstinit)};
11481
0
              }
11482
2.20k
              if (check_capacity && dstend - dst < 2) {
11483
0
                return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11484
0
                        size_t(dst - dstinit)};
11485
0
              }
11486
2.20k
              *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11487
2.20k
              *dst++ = static_cast<char>((triple >> 8) & 0xFF);
11488
19.2k
            } else if (!ignore_garbage && idx == 1 &&
11489
38
                       (!is_partial(last_chunk_options) ||
11490
0
                        (is_partial(last_chunk_options) &&
11491
38
                         padding_characters > 0))) {
11492
38
              return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
11493
38
                      size_t(dst - dstinit)};
11494
19.1k
            } else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
11495
21
              return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11496
21
                      size_t(dst - dstinit), true};
11497
21
            }
11498
21.6k
            return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
11499
21.7k
          }
11500
21.7k
    }
11501
109
    if (check_capacity && dstend - dst < 3) {
11502
0
      return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11503
0
              size_t(dst - dstinit)};
11504
0
    }
11505
109
    uint32_t triple =
11506
109
        (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
11507
109
        (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
11508
109
    *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11509
109
    *dst++ = static_cast<char>((triple >> 8) & 0xFF);
11510
109
    *dst++ = static_cast<char>(triple & 0xFF);
11511
109
  }
11512
21.8k
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_impl<true, char>(char*, unsigned long, char const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_impl<true, char16_t>(char*, unsigned long, char16_t const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_impl<false, char>(char*, unsigned long, char const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
11312
21.8k
    base64_options options, last_chunk_handling_options last_chunk_options) {
11313
21.8k
  char *dstend = dst + outlen;
11314
21.8k
  (void)dstend;
11315
  // This looks like 10 branches, but we expect the compiler to resolve this to
11316
  // two branches (easily predicted):
11317
21.8k
  const uint8_t *to_base64 =
11318
21.8k
      (options & base64_default_or_url)
11319
21.8k
          ? tables::base64::to_base64_default_or_url_value
11320
21.8k
          : ((options & base64_url) ? tables::base64::to_base64_url_value
11321
21.8k
                                    : tables::base64::to_base64_value);
11322
21.8k
  const uint32_t *d0 =
11323
21.8k
      (options & base64_default_or_url)
11324
21.8k
          ? tables::base64::base64_default_or_url::d0
11325
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d0
11326
21.8k
                                    : tables::base64::base64_default::d0);
11327
21.8k
  const uint32_t *d1 =
11328
21.8k
      (options & base64_default_or_url)
11329
21.8k
          ? tables::base64::base64_default_or_url::d1
11330
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d1
11331
21.8k
                                    : tables::base64::base64_default::d1);
11332
21.8k
  const uint32_t *d2 =
11333
21.8k
      (options & base64_default_or_url)
11334
21.8k
          ? tables::base64::base64_default_or_url::d2
11335
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d2
11336
21.8k
                                    : tables::base64::base64_default::d2);
11337
21.8k
  const uint32_t *d3 =
11338
21.8k
      (options & base64_default_or_url)
11339
21.8k
          ? tables::base64::base64_default_or_url::d3
11340
21.8k
          : ((options & base64_url) ? tables::base64::base64_url::d3
11341
21.8k
                                    : tables::base64::base64_default::d3);
11342
21.8k
  const bool ignore_garbage =
11343
21.8k
      (options == base64_options::base64_url_accept_garbage) ||
11344
21.8k
      (options == base64_options::base64_default_accept_garbage) ||
11345
21.8k
      (options == base64_options::base64_default_or_url_accept_garbage);
11346
11347
21.8k
  const char_type *srcend = src + length;
11348
21.8k
  const char_type *srcinit = src;
11349
21.8k
  const char *dstinit = dst;
11350
11351
21.8k
  uint32_t x;
11352
21.8k
  size_t idx;
11353
21.8k
  uint8_t buffer[4];
11354
21.9k
  while (true) {
11355
97.2k
    while (srcend - src >= 4 && is_eight_byte(src[0]) &&
11356
75.5k
           is_eight_byte(src[1]) && is_eight_byte(src[2]) &&
11357
75.5k
           is_eight_byte(src[3]) &&
11358
75.5k
           (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
11359
75.5k
                d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
11360
75.3k
      if (check_capacity && dstend - dst < 3) {
11361
0
        return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit),
11362
0
                size_t(dst - dstinit)};
11363
0
      }
11364
75.3k
      *dst++ = static_cast<char>(x & 0xFF);
11365
75.3k
      *dst++ = static_cast<char>((x >> 8) & 0xFF);
11366
75.3k
      *dst++ = static_cast<char>((x >> 16) & 0xFF);
11367
75.3k
      src += 4;
11368
75.3k
    }
11369
21.9k
    const char_type *srccur = src;
11370
21.9k
    idx = 0;
11371
    // we need at least four characters.
11372
21.9k
#ifdef __clang__
11373
    // If possible, we read four characters at a time. (It is an optimization.)
11374
21.9k
    if (ignore_garbage && src + 4 <= srcend) {
11375
0
      char_type c0 = src[0];
11376
0
      char_type c1 = src[1];
11377
0
      char_type c2 = src[2];
11378
0
      char_type c3 = src[3];
11379
11380
0
      uint8_t code0 = to_base64[uint8_t(c0)];
11381
0
      uint8_t code1 = to_base64[uint8_t(c1)];
11382
0
      uint8_t code2 = to_base64[uint8_t(c2)];
11383
0
      uint8_t code3 = to_base64[uint8_t(c3)];
11384
11385
0
      buffer[idx] = code0;
11386
0
      idx += (is_eight_byte(c0) && code0 <= 63);
11387
0
      buffer[idx] = code1;
11388
0
      idx += (is_eight_byte(c1) && code1 <= 63);
11389
0
      buffer[idx] = code2;
11390
0
      idx += (is_eight_byte(c2) && code2 <= 63);
11391
0
      buffer[idx] = code3;
11392
0
      idx += (is_eight_byte(c3) && code3 <= 63);
11393
0
      src += 4;
11394
0
    }
11395
21.9k
#endif
11396
32.5k
    while ((idx < 4) && (src < srcend)) {
11397
10.6k
      char_type c = *src;
11398
11399
10.6k
      uint8_t code = to_base64[uint8_t(c)];
11400
10.6k
      buffer[idx] = uint8_t(code);
11401
10.6k
      if (is_eight_byte(c) && code <= 63) {
11402
7.85k
        idx++;
11403
7.85k
      } else if (!ignore_garbage &&
11404
2.83k
                 (code > 64 || !scalar::base64::is_eight_byte(c))) {
11405
101
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11406
101
                size_t(dst - dstinit)};
11407
2.73k
      } else {
11408
        // We have a space or a newline or garbage. We ignore it.
11409
2.73k
      }
11410
10.5k
      src++;
11411
10.5k
    }
11412
21.8k
    if (idx != 4) {
11413
21.7k
      simdutf_log_assert(idx < 4, "idx should be less than 4");
11414
      // We never should have that the number of base64 characters + the
11415
      // number of padding characters is more than 4.
11416
21.7k
      if (!ignore_garbage && (idx + padding_characters > 4)) {
11417
1
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11418
1
                size_t(dst - dstinit), true};
11419
1
      }
11420
11421
      // The idea here is that in loose mode,
11422
      // if there is padding at all, it must be used
11423
      // to form 4-wise chunk. However, in loose mode,
11424
      // we do accept no padding at all.
11425
21.7k
      if (!ignore_garbage &&
11426
21.7k
          last_chunk_options == last_chunk_handling_options::loose &&
11427
21.7k
          (idx >= 2) && padding_characters > 0 &&
11428
2.46k
          ((idx + padding_characters) & 3) != 0) {
11429
12
        return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11430
12
                size_t(dst - dstinit), true};
11431
12
      } else
11432
11433
        // The idea here is that in strict mode, we do not want to accept
11434
        // incomplete base64 chunks. So if the chunk was otherwise valid, we
11435
        // return BASE64_INPUT_REMAINDER.
11436
21.7k
        if (!ignore_garbage &&
11437
21.7k
            last_chunk_options == last_chunk_handling_options::strict &&
11438
0
            (idx >= 2) && ((idx + padding_characters) & 3) != 0) {
11439
          // The partial chunk was at src - idx
11440
0
          return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
11441
0
                  size_t(dst - dstinit), true};
11442
0
        } else
11443
          // If there is a partial chunk with insufficient padding, with
11444
          // stop_before_partial, we need to just ignore it. In "only full"
11445
          // mode, skip the minute there are padding characters.
11446
21.7k
          if ((last_chunk_options ==
11447
21.7k
                   last_chunk_handling_options::stop_before_partial &&
11448
0
               (padding_characters + idx < 4) && (idx != 0) &&
11449
0
               (idx >= 2 || padding_characters == 0)) ||
11450
21.7k
              (last_chunk_options ==
11451
21.7k
                   last_chunk_handling_options::only_full_chunks &&
11452
0
               (idx >= 2 || padding_characters == 0))) {
11453
            // partial means that we are *not* going to consume the read
11454
            // characters. We need to rewind the src pointer.
11455
0
            src = srccur;
11456
0
            return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
11457
21.7k
          } else {
11458
21.7k
            if (idx == 2) {
11459
308
              uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
11460
308
                                (uint32_t(buffer[1]) << 2 * 6);
11461
308
              if (!ignore_garbage &&
11462
308
                  (last_chunk_options == last_chunk_handling_options::strict) &&
11463
0
                  (triple & 0xffff)) {
11464
0
                return {BASE64_EXTRA_BITS, size_t(src - srcinit),
11465
0
                        size_t(dst - dstinit)};
11466
0
              }
11467
308
              if (check_capacity && dstend - dst < 1) {
11468
0
                return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11469
0
                        size_t(dst - dstinit)};
11470
0
              }
11471
308
              *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11472
21.4k
            } else if (idx == 3) {
11473
2.20k
              uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
11474
2.20k
                                (uint32_t(buffer[1]) << 2 * 6) +
11475
2.20k
                                (uint32_t(buffer[2]) << 1 * 6);
11476
2.20k
              if (!ignore_garbage &&
11477
2.20k
                  (last_chunk_options == last_chunk_handling_options::strict) &&
11478
0
                  (triple & 0xff)) {
11479
0
                return {BASE64_EXTRA_BITS, size_t(src - srcinit),
11480
0
                        size_t(dst - dstinit)};
11481
0
              }
11482
2.20k
              if (check_capacity && dstend - dst < 2) {
11483
0
                return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11484
0
                        size_t(dst - dstinit)};
11485
0
              }
11486
2.20k
              *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11487
2.20k
              *dst++ = static_cast<char>((triple >> 8) & 0xFF);
11488
19.2k
            } else if (!ignore_garbage && idx == 1 &&
11489
38
                       (!is_partial(last_chunk_options) ||
11490
0
                        (is_partial(last_chunk_options) &&
11491
38
                         padding_characters > 0))) {
11492
38
              return {BASE64_INPUT_REMAINDER, size_t(src - srcinit),
11493
38
                      size_t(dst - dstinit)};
11494
19.1k
            } else if (!ignore_garbage && idx == 0 && padding_characters > 0) {
11495
21
              return {INVALID_BASE64_CHARACTER, size_t(src - srcinit),
11496
21
                      size_t(dst - dstinit), true};
11497
21
            }
11498
21.6k
            return {SUCCESS, size_t(src - srcinit), size_t(dst - dstinit)};
11499
21.7k
          }
11500
21.7k
    }
11501
109
    if (check_capacity && dstend - dst < 3) {
11502
0
      return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit),
11503
0
              size_t(dst - dstinit)};
11504
0
    }
11505
109
    uint32_t triple =
11506
109
        (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
11507
109
        (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
11508
109
    *dst++ = static_cast<char>((triple >> 16) & 0xFF);
11509
109
    *dst++ = static_cast<char>((triple >> 8) & 0xFF);
11510
109
    *dst++ = static_cast<char>(triple & 0xFF);
11511
109
  }
11512
21.8k
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_impl<false, char16_t>(char*, unsigned long, char16_t const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
11513
11514
template <class char_type>
11515
simdutf_constexpr23 full_result base64_tail_decode(
11516
    char *dst, const char_type *src, size_t length,
11517
    size_t padding_characters, // number of padding characters
11518
                               // '=', typically 0, 1, 2.
11519
21.8k
    base64_options options, last_chunk_handling_options last_chunk_options) {
11520
21.8k
  return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
11521
21.8k
                                        options, last_chunk_options);
11522
21.8k
}
simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode<char>(char*, char const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Line
Count
Source
11519
21.8k
    base64_options options, last_chunk_handling_options last_chunk_options) {
11520
21.8k
  return base64_tail_decode_impl<false>(dst, 0, src, length, padding_characters,
11521
21.8k
                                        options, last_chunk_options);
11522
21.8k
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode<char16_t>(char*, char16_t const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
11523
11524
// like base64_tail_decode, but it will not write past the end of the output
11525
// buffer. The outlen parameter is modified to reflect the number of bytes
11526
// written. This functions assumes that the padding (=) has been removed.
11527
//
11528
template <class char_type>
11529
simdutf_constexpr23 full_result base64_tail_decode_safe(
11530
    char *dst, size_t outlen, const char_type *src, size_t length,
11531
    size_t padding_characters, // number of padding characters
11532
                               // '=', typically 0, 1, 2.
11533
0
    base64_options options, last_chunk_handling_options last_chunk_options) {
11534
0
  return base64_tail_decode_impl<true>(dst, outlen, src, length,
11535
0
                                       padding_characters, options,
11536
0
                                       last_chunk_options);
11537
0
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_safe<char>(char*, unsigned long, char const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_tail_decode_safe<char16_t>(char*, unsigned long, char16_t const*, unsigned long, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
11538
11539
inline simdutf_constexpr23 full_result
11540
patch_tail_result(full_result r, size_t previous_input, size_t previous_output,
11541
                  size_t equallocation, size_t full_input_length,
11542
21.8k
                  last_chunk_handling_options last_chunk_options) {
11543
21.8k
  r.input_count += previous_input;
11544
21.8k
  r.output_count += previous_output;
11545
21.8k
  if (r.padding_error) {
11546
34
    r.input_count = equallocation;
11547
34
  }
11548
11549
21.8k
  if (r.error == error_code::SUCCESS) {
11550
21.6k
    if (!is_partial(last_chunk_options)) {
11551
      // A success when we are not in stop_before_partial mode.
11552
      // means that we have consumed the whole input buffer.
11553
21.6k
      r.input_count = full_input_length;
11554
21.6k
    } else if (r.output_count % 3 != 0) {
11555
0
      r.input_count = full_input_length;
11556
0
    }
11557
21.6k
  }
11558
21.8k
  return r;
11559
21.8k
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::base64::patch_tail_result(simdutf::full_result, unsigned long, unsigned long, unsigned long, unsigned long, simdutf::last_chunk_handling_options)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::base64::patch_tail_result(simdutf::full_result, unsigned long, unsigned long, unsigned long, unsigned long, simdutf::last_chunk_handling_options)
simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::patch_tail_result(simdutf::full_result, unsigned long, unsigned long, unsigned long, unsigned long, simdutf::last_chunk_handling_options)
Line
Count
Source
11542
21.8k
                  last_chunk_handling_options last_chunk_options) {
11543
21.8k
  r.input_count += previous_input;
11544
21.8k
  r.output_count += previous_output;
11545
21.8k
  if (r.padding_error) {
11546
34
    r.input_count = equallocation;
11547
34
  }
11548
11549
21.8k
  if (r.error == error_code::SUCCESS) {
11550
21.6k
    if (!is_partial(last_chunk_options)) {
11551
      // A success when we are not in stop_before_partial mode.
11552
      // means that we have consumed the whole input buffer.
11553
21.6k
      r.input_count = full_input_length;
11554
21.6k
    } else if (r.output_count % 3 != 0) {
11555
0
      r.input_count = full_input_length;
11556
0
    }
11557
21.6k
  }
11558
21.8k
  return r;
11559
21.8k
}
11560
11561
// Returns the number of bytes written. The destination buffer must be large
11562
// enough. It will add padding (=) if needed.
11563
template <bool use_lines = false>
11564
simdutf_constexpr23 size_t tail_encode_base64_impl(
11565
    char *dst, const char *src, size_t srclen, base64_options options,
11566
0
    size_t line_length = simdutf::default_line_length, size_t line_offset = 0) {
11567
0
  if constexpr (use_lines) {
11568
    // sanitize line_length and starting_line_offset.
11569
    // line_length must be greater than 3.
11570
0
    if (line_length < 4) {
11571
0
      line_length = 4;
11572
0
    }
11573
0
    simdutf_log_assert(line_offset <= line_length,
11574
0
                       "line_offset should be less than line_length");
11575
0
  }
11576
  // By default, we use padding if we are not using the URL variant.
11577
  // This is check with ((options & base64_url) == 0) which returns true if we
11578
  // are not using the URL variant. However, we also allow 'inversion' of the
11579
  // convention with the base64_reverse_padding option. If the
11580
  // base64_reverse_padding option is set, we use padding if we are using the
11581
  // URL variant, and we omit it if we are not using the URL variant. This is
11582
  // checked with
11583
  // ((options & base64_reverse_padding) == base64_reverse_padding).
11584
0
  bool use_padding =
11585
0
      ((options & base64_url) == 0) ^
11586
0
      ((options & base64_reverse_padding) == base64_reverse_padding);
11587
  // This looks like 3 branches, but we expect the compiler to resolve this to
11588
  // a single branch:
11589
0
  const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0
11590
0
                                          : tables::base64::base64_default::e0;
11591
0
  const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1
11592
0
                                          : tables::base64::base64_default::e1;
11593
0
  const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2
11594
0
                                          : tables::base64::base64_default::e2;
11595
0
  char *out = dst;
11596
0
  size_t i = 0;
11597
0
  uint8_t t1, t2, t3;
11598
0
  for (; i + 2 < srclen; i += 3) {
11599
0
    t1 = uint8_t(src[i]);
11600
0
    t2 = uint8_t(src[i + 1]);
11601
0
    t3 = uint8_t(src[i + 2]);
11602
0
    if constexpr (use_lines) {
11603
0
      if (line_offset + 3 >= line_length) {
11604
0
        if (line_offset == line_length) {
11605
0
          *out++ = '\n';
11606
0
          *out++ = e0[t1];
11607
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11608
0
          *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11609
0
          *out++ = e2[t3];
11610
0
          line_offset = 4;
11611
0
        } else if (line_offset + 1 == line_length) {
11612
0
          *out++ = e0[t1];
11613
0
          *out++ = '\n';
11614
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11615
0
          *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11616
0
          *out++ = e2[t3];
11617
0
          line_offset = 3;
11618
0
        } else if (line_offset + 2 == line_length) {
11619
0
          *out++ = e0[t1];
11620
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11621
0
          *out++ = '\n';
11622
0
          *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11623
0
          *out++ = e2[t3];
11624
0
          line_offset = 2;
11625
0
        } else if (line_offset + 3 == line_length) {
11626
0
          *out++ = e0[t1];
11627
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11628
0
          *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11629
0
          *out++ = '\n';
11630
0
          *out++ = e2[t3];
11631
0
          line_offset = 1;
11632
0
        }
11633
0
      } else {
11634
0
        *out++ = e0[t1];
11635
0
        *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11636
0
        *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11637
0
        *out++ = e2[t3];
11638
0
        line_offset += 4;
11639
0
      }
11640
0
    } else {
11641
0
      *out++ = e0[t1];
11642
0
      *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11643
0
      *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
11644
0
      *out++ = e2[t3];
11645
0
    }
11646
0
  }
11647
0
  switch (srclen - i) {
11648
0
  case 0:
11649
0
    break;
11650
0
  case 1:
11651
0
    t1 = uint8_t(src[i]);
11652
0
    if constexpr (use_lines) {
11653
0
      if (use_padding) {
11654
0
        if (line_offset + 3 >= line_length) {
11655
0
          if (line_offset == line_length) {
11656
0
            *out++ = '\n';
11657
0
            *out++ = e0[t1];
11658
0
            *out++ = e1[(t1 & 0x03) << 4];
11659
0
            *out++ = '=';
11660
0
            *out++ = '=';
11661
0
          } else if (line_offset + 1 == line_length) {
11662
0
            *out++ = e0[t1];
11663
0
            *out++ = '\n';
11664
0
            *out++ = e1[(t1 & 0x03) << 4];
11665
0
            *out++ = '=';
11666
0
            *out++ = '=';
11667
0
          } else if (line_offset + 2 == line_length) {
11668
0
            *out++ = e0[t1];
11669
0
            *out++ = e1[(t1 & 0x03) << 4];
11670
0
            *out++ = '\n';
11671
0
            *out++ = '=';
11672
0
            *out++ = '=';
11673
0
          } else if (line_offset + 3 == line_length) {
11674
0
            *out++ = e0[t1];
11675
0
            *out++ = e1[(t1 & 0x03) << 4];
11676
0
            *out++ = '=';
11677
0
            *out++ = '\n';
11678
0
            *out++ = '=';
11679
0
          }
11680
0
        } else {
11681
0
          *out++ = e0[t1];
11682
0
          *out++ = e1[(t1 & 0x03) << 4];
11683
0
          *out++ = '=';
11684
0
          *out++ = '=';
11685
0
        }
11686
0
      } else {
11687
0
        if (line_offset + 2 >= line_length) {
11688
0
          if (line_offset == line_length) {
11689
0
            *out++ = '\n';
11690
0
            *out++ = e0[uint8_t(src[i])];
11691
0
            *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
11692
0
          } else if (line_offset + 1 == line_length) {
11693
0
            *out++ = e0[uint8_t(src[i])];
11694
0
            *out++ = '\n';
11695
0
            *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
11696
0
          } else {
11697
0
            *out++ = e0[uint8_t(src[i])];
11698
0
            *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
11699
            // *out++ = '\n'; ==> no newline at the end of the output
11700
0
          }
11701
0
        } else {
11702
0
          *out++ = e0[uint8_t(src[i])];
11703
0
          *out++ = e1[(uint8_t(src[i]) & 0x03) << 4];
11704
0
        }
11705
0
      }
11706
0
    } else {
11707
0
      *out++ = e0[t1];
11708
0
      *out++ = e1[(t1 & 0x03) << 4];
11709
0
      if (use_padding) {
11710
0
        *out++ = '=';
11711
0
        *out++ = '=';
11712
0
      }
11713
0
    }
11714
0
    break;
11715
0
  default: /* case 2 */
11716
0
    t1 = uint8_t(src[i]);
11717
0
    t2 = uint8_t(src[i + 1]);
11718
0
    if constexpr (use_lines) {
11719
0
      if (use_padding) {
11720
0
        if (line_offset + 3 >= line_length) {
11721
0
          if (line_offset == line_length) {
11722
0
            *out++ = '\n';
11723
0
            *out++ = e0[t1];
11724
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11725
0
            *out++ = e2[(t2 & 0x0F) << 2];
11726
0
            *out++ = '=';
11727
0
          } else if (line_offset + 1 == line_length) {
11728
0
            *out++ = e0[t1];
11729
0
            *out++ = '\n';
11730
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11731
0
            *out++ = e2[(t2 & 0x0F) << 2];
11732
0
            *out++ = '=';
11733
0
          } else if (line_offset + 2 == line_length) {
11734
0
            *out++ = e0[t1];
11735
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11736
0
            *out++ = '\n';
11737
0
            *out++ = e2[(t2 & 0x0F) << 2];
11738
0
            *out++ = '=';
11739
0
          } else if (line_offset + 3 == line_length) {
11740
0
            *out++ = e0[t1];
11741
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11742
0
            *out++ = e2[(t2 & 0x0F) << 2];
11743
0
            *out++ = '\n';
11744
0
            *out++ = '=';
11745
0
          }
11746
0
        } else {
11747
0
          *out++ = e0[t1];
11748
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11749
0
          *out++ = e2[(t2 & 0x0F) << 2];
11750
0
          *out++ = '=';
11751
0
        }
11752
0
      } else {
11753
0
        if (line_offset + 3 >= line_length) {
11754
0
          if (line_offset == line_length) {
11755
0
            *out++ = '\n';
11756
0
            *out++ = e0[t1];
11757
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11758
0
            *out++ = e2[(t2 & 0x0F) << 2];
11759
0
          } else if (line_offset + 1 == line_length) {
11760
0
            *out++ = e0[t1];
11761
0
            *out++ = '\n';
11762
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11763
0
            *out++ = e2[(t2 & 0x0F) << 2];
11764
0
          } else if (line_offset + 2 == line_length) {
11765
0
            *out++ = e0[t1];
11766
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11767
0
            *out++ = '\n';
11768
0
            *out++ = e2[(t2 & 0x0F) << 2];
11769
0
          } else {
11770
0
            *out++ = e0[t1];
11771
0
            *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11772
0
            *out++ = e2[(t2 & 0x0F) << 2];
11773
            // *out++ = '\n'; ==> no newline at the end of the output
11774
0
          }
11775
0
        } else {
11776
0
          *out++ = e0[t1];
11777
0
          *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11778
0
          *out++ = e2[(t2 & 0x0F) << 2];
11779
0
        }
11780
0
      }
11781
0
    } else {
11782
0
      *out++ = e0[t1];
11783
0
      *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
11784
0
      *out++ = e2[(t2 & 0x0F) << 2];
11785
0
      if (use_padding) {
11786
0
        *out++ = '=';
11787
0
      }
11788
0
    }
11789
0
  }
11790
0
  return (size_t)(out - dst);
11791
0
}
Unexecuted instantiation: parser.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64_impl<false>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long, unsigned long)
Unexecuted instantiation: base64.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64_impl<false>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long, unsigned long)
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64_impl<false>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long, unsigned long)
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64_impl<true>(char*, char const*, unsigned long, simdutf::base64_options, unsigned long, unsigned long)
11792
11793
// Returns the number of bytes written. The destination buffer must be large
11794
// enough. It will add padding (=) if needed.
11795
simdutf_unused inline simdutf_constexpr23 size_t tail_encode_base64(
11796
0
    char *dst, const char *src, size_t srclen, base64_options options) {
11797
0
  return tail_encode_base64_impl(dst, src, srclen, options);
11798
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64(char*, char const*, unsigned long, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64(char*, char const*, unsigned long, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::tail_encode_base64(char*, char const*, unsigned long, simdutf::base64_options)
11799
11800
template <class InputPtr>
11801
simdutf_warn_unused simdutf_constexpr23 size_t
11802
0
maximal_binary_length_from_base64(InputPtr input, size_t length) noexcept {
11803
  // We process the padding characters ('=') at the end to make sure
11804
  // that we return an exact result when the input has no ignorable characters
11805
  // (e.g., spaces).
11806
0
  size_t padding = 0;
11807
0
  if (length > 0) {
11808
0
    if (input[length - 1] == '=') {
11809
0
      padding++;
11810
0
      if (length > 1 && input[length - 2] == '=') {
11811
0
        padding++;
11812
0
      }
11813
0
    }
11814
0
  }
11815
  // The input is not otherwise processed for ignorable characters or
11816
  // validation, so that the function runs in constant time (very fast). In
11817
  // practice, base64 inputs without ignorable characters are common and the
11818
  // common case are line separated inputs with relatively long lines (e.g., 76
11819
  // characters) which leads this function to a slight (1%) overestimation of
11820
  // the output size.
11821
  //
11822
  // Of course, some inputs might contain an arbitrary number of spaces or
11823
  // newlines, which would make this function return a very pessimistic output
11824
  // size but systems that produce base64 outputs typically do not do that and
11825
  // if they do, they do not care much about minimizing memory usage.
11826
  //
11827
  // In specialized applications, users may know that their input is line
11828
  // separated, which can be checked very quickly by by iterating (e.g., over 76
11829
  // character chunks, looking for the linefeed characters only). We could
11830
  // provide a specialized function for that, but it is not clear that the added
11831
  // complexity is worth it for us.
11832
  //
11833
0
  size_t actual_length = length - padding;
11834
0
  if (actual_length % 4 <= 1) {
11835
0
    return actual_length / 4 * 3;
11836
0
  }
11837
  // if we have a valid input, then the remainder must be 2 or 3 adding one or
11838
  // two extra bytes.
11839
0
  return actual_length / 4 * 3 + (actual_length % 4) - 1;
11840
0
}
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::maximal_binary_length_from_base64<char const*>(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::maximal_binary_length_from_base64<char16_t const*>(char16_t const*, unsigned long)
11841
11842
// This function computes the binary length by iterating through the input
11843
// and counting non-whitespace characters (excluding padding characters).
11844
// We use a simple check (c > ' ') which is easy to parallelize and matches
11845
// SIMD behavior. Only the last few characters are checked for padding '='.
11846
template <class char_type>
11847
simdutf_warn_unused simdutf_constexpr23 size_t
11848
0
binary_length_from_base64(const char_type *input, size_t length) noexcept {
11849
  // Count non-whitespace characters (c > ' ') with loop unrolling
11850
0
  size_t count = 0;
11851
0
  for (size_t i = 0; i < length; i++) {
11852
0
    count += (input[i] > ' ');
11853
0
  }
11854
11855
  // Check for padding '=' at the end (at most 2 padding characters)
11856
  // Scan backwards, skipping whitespace, to find padding
11857
0
  size_t padding = 0;
11858
0
  size_t pos = length;
11859
  // Skip trailing whitespace
11860
0
  while (pos > 0 && padding < 2) {
11861
0
    char_type c = input[--pos];
11862
0
    if (c == '=') {
11863
0
      padding++;
11864
0
    } else if (c > ' ') {
11865
0
      break;
11866
0
    }
11867
0
  }
11868
0
  return ((count - padding) * 3) / 4;
11869
0
}
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::binary_length_from_base64<char>(char const*, unsigned long)
Unexecuted instantiation: simdutf.cpp:unsigned long simdutf::scalar::(anonymous namespace)::base64::binary_length_from_base64<char16_t>(char16_t const*, unsigned long)
11870
11871
template <typename char_type>
11872
simdutf_warn_unused simdutf_constexpr23 full_result
11873
base64_to_binary_details_impl(
11874
    const char_type *input, size_t length, char *output, base64_options options,
11875
0
    last_chunk_handling_options last_chunk_options) noexcept {
11876
0
  const bool ignore_garbage =
11877
0
      (options == base64_options::base64_url_accept_garbage) ||
11878
0
      (options == base64_options::base64_default_accept_garbage) ||
11879
0
      (options == base64_options::base64_default_or_url_accept_garbage);
11880
0
  auto ri = simdutf::scalar::base64::find_end(input, length, options);
11881
0
  size_t equallocation = ri.equallocation;
11882
0
  size_t equalsigns = ri.equalsigns;
11883
0
  length = ri.srclen;
11884
0
  size_t full_input_length = ri.full_input_length;
11885
0
  if (length == 0) {
11886
0
    if (!ignore_garbage && equalsigns > 0) {
11887
0
      return {INVALID_BASE64_CHARACTER, equallocation, 0, true};
11888
0
    }
11889
0
    return {SUCCESS, full_input_length, 0};
11890
0
  }
11891
0
  full_result r = scalar::base64::base64_tail_decode(
11892
0
      output, input, length, equalsigns, options, last_chunk_options);
11893
0
  r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
11894
0
                                        full_input_length, last_chunk_options);
11895
0
  if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
11896
0
      equalsigns > 0 && !ignore_garbage) {
11897
    // additional checks
11898
0
    if ((r.output_count % 3 == 0) ||
11899
0
        ((r.output_count % 3) + 1 + equalsigns != 4)) {
11900
0
      return {INVALID_BASE64_CHARACTER, equallocation, r.output_count, true};
11901
0
    }
11902
0
  }
11903
  // When is_partial(last_chunk_options) is true, we must either end with
11904
  // the end of the stream (beyond whitespace) or right after a non-ignorable
11905
  // character or at the very beginning of the stream.
11906
  // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
11907
0
  if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
11908
0
      r.input_count < full_input_length) {
11909
    // First check if we can extend the input to the end of the stream
11910
0
    while (r.input_count < full_input_length &&
11911
0
           base64_ignorable(*(input + r.input_count), options)) {
11912
0
      r.input_count++;
11913
0
    }
11914
    // If we are still not at the end of the stream, then we must backtrack
11915
    // to the last non-ignorable character.
11916
0
    if (r.input_count < full_input_length) {
11917
0
      while (r.input_count > 0 &&
11918
0
             base64_ignorable(*(input + r.input_count - 1), options)) {
11919
0
        r.input_count--;
11920
0
      }
11921
0
    }
11922
0
  }
11923
0
  return r;
11924
0
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_to_binary_details_impl<char>(char const*, unsigned long, char*, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_to_binary_details_impl<char16_t>(char16_t const*, unsigned long, char*, simdutf::base64_options, simdutf::last_chunk_handling_options)
11925
11926
template <typename char_type>
11927
simdutf_constexpr23 simdutf_warn_unused full_result
11928
base64_to_binary_details_safe_impl(
11929
    const char_type *input, size_t length, char *output, size_t outlen,
11930
    base64_options options,
11931
0
    last_chunk_handling_options last_chunk_options) noexcept {
11932
0
  const bool ignore_garbage =
11933
0
      (options == base64_options::base64_url_accept_garbage) ||
11934
0
      (options == base64_options::base64_default_accept_garbage) ||
11935
0
      (options == base64_options::base64_default_or_url_accept_garbage);
11936
0
  auto ri = simdutf::scalar::base64::find_end(input, length, options);
11937
0
  size_t equallocation = ri.equallocation;
11938
0
  size_t equalsigns = ri.equalsigns;
11939
0
  length = ri.srclen;
11940
0
  size_t full_input_length = ri.full_input_length;
11941
0
  if (length == 0) {
11942
0
    if (!ignore_garbage && equalsigns > 0) {
11943
0
      return {INVALID_BASE64_CHARACTER, equallocation, 0};
11944
0
    }
11945
0
    return {SUCCESS, full_input_length, 0};
11946
0
  }
11947
0
  full_result r = scalar::base64::base64_tail_decode_safe(
11948
0
      output, outlen, input, length, equalsigns, options, last_chunk_options);
11949
0
  r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
11950
0
                                        full_input_length, last_chunk_options);
11951
0
  if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
11952
0
      equalsigns > 0 && !ignore_garbage) {
11953
    // additional checks
11954
0
    if ((r.output_count % 3 == 0) ||
11955
0
        ((r.output_count % 3) + 1 + equalsigns != 4)) {
11956
0
      return {INVALID_BASE64_CHARACTER, equallocation, r.output_count};
11957
0
    }
11958
0
  }
11959
11960
  // When is_partial(last_chunk_options) is true, we must either end with
11961
  // the end of the stream (beyond whitespace) or right after a non-ignorable
11962
  // character or at the very beginning of the stream.
11963
  // See https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
11964
0
  if (is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
11965
0
      r.input_count < full_input_length) {
11966
    // First check if we can extend the input to the end of the stream
11967
0
    while (r.input_count < full_input_length &&
11968
0
           base64_ignorable(*(input + r.input_count), options)) {
11969
0
      r.input_count++;
11970
0
    }
11971
    // If we are still not at the end of the stream, then we must backtrack
11972
    // to the last non-ignorable character.
11973
0
    if (r.input_count < full_input_length) {
11974
0
      while (r.input_count > 0 &&
11975
0
             base64_ignorable(*(input + r.input_count - 1), options)) {
11976
0
        r.input_count--;
11977
0
      }
11978
0
    }
11979
0
  }
11980
0
  return r;
11981
0
}
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_to_binary_details_safe_impl<char>(char const*, unsigned long, char*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf.cpp:simdutf::full_result simdutf::scalar::(anonymous namespace)::base64::base64_to_binary_details_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long, simdutf::base64_options, simdutf::last_chunk_handling_options)
11982
11983
simdutf_warn_unused simdutf_constexpr23 size_t
11984
0
base64_length_from_binary(size_t length, base64_options options) noexcept {
11985
  // By default, we use padding if we are not using the URL variant.
11986
  // This is check with ((options & base64_url) == 0) which returns true if we
11987
  // are not using the URL variant. However, we also allow 'inversion' of the
11988
  // convention with the base64_reverse_padding option. If the
11989
  // base64_reverse_padding option is set, we use padding if we are using the
11990
  // URL variant, and we omit it if we are not using the URL variant. This is
11991
  // checked with
11992
  // ((options & base64_reverse_padding) == base64_reverse_padding).
11993
0
  bool use_padding =
11994
0
      ((options & base64_url) == 0) ^
11995
0
      ((options & base64_reverse_padding) == base64_reverse_padding);
11996
0
  if (!use_padding) {
11997
0
    return length / 3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
11998
0
  }
11999
0
  return (length + 2) / 3 *
12000
0
         4; // We use padding to make the length a multiple of 4.
12001
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary(unsigned long, simdutf::base64_options)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary(unsigned long, simdutf::base64_options)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary(unsigned long, simdutf::base64_options)
12002
12003
simdutf_warn_unused simdutf_constexpr23 size_t
12004
base64_length_from_binary_with_lines(size_t length, base64_options options,
12005
0
                                     size_t line_length) noexcept {
12006
0
  if (length == 0) {
12007
0
    return 0;
12008
0
  }
12009
0
  size_t base64_length =
12010
0
      scalar::base64::base64_length_from_binary(length, options);
12011
0
  if (line_length < 4) {
12012
0
    line_length = 4;
12013
0
  }
12014
0
  // number of lines, rounded up without computing base64_length + line_length,
12015
0
  // which wraps around when line_length is close to SIZE_MAX
12016
0
  size_t lines = base64_length / line_length;
12017
0
  if (base64_length % line_length != 0) {
12018
0
    lines++;
12019
0
  }
12020
0
  return base64_length + lines - 1;
12021
0
}
Unexecuted instantiation: parser.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary_with_lines(unsigned long, simdutf::base64_options, unsigned long)
Unexecuted instantiation: base64.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary_with_lines(unsigned long, simdutf::base64_options, unsigned long)
Unexecuted instantiation: simdutf.cpp:simdutf::scalar::(anonymous namespace)::base64::base64_length_from_binary_with_lines(unsigned long, simdutf::base64_options, unsigned long)
12022
12023
// Return the length of the prefix that contains count base64 characters.
12024
// Thus, if count is 3, the function returns the length of the prefix
12025
// that contains 3 base64 characters.
12026
// The function returns (size_t)-1 if there is not enough base64 characters in
12027
// the input.
12028
template <typename char_type>
12029
simdutf_warn_unused size_t prefix_length(size_t count,
12030
                                         simdutf::base64_options options,
12031
                                         const char_type *input,
12032
                                         size_t length) noexcept {
12033
  size_t i = 0;
12034
  while (i < length && is_ignorable(input[i], options)) {
12035
    i++;
12036
  }
12037
  if (count == 0) {
12038
    return i; // duh!
12039
  }
12040
  for (; i < length; i++) {
12041
    if (is_ignorable(input[i], options)) {
12042
      continue;
12043
    }
12044
    // We have a base64 character or a padding character.
12045
    count--;
12046
    if (count == 0) {
12047
      return i + 1;
12048
    }
12049
  }
12050
  simdutf_log_assert(false, "You never get here");
12051
12052
  return -1; // should never happen
12053
}
12054
12055
} // namespace base64
12056
} // unnamed namespace
12057
} // namespace scalar
12058
} // namespace simdutf
12059
12060
#endif
12061
/* end file include/simdutf/scalar/base64.h */
12062
12063
namespace simdutf {
12064
12065
0
inline std::string_view to_string(base64_options options) {
12066
0
  switch (options) {
12067
0
  case base64_default:
12068
0
    return "base64_default";
12069
0
  case base64_url:
12070
0
    return "base64_url";
12071
0
  case base64_reverse_padding:
12072
0
    return "base64_reverse_padding";
12073
0
  case base64_url_with_padding:
12074
0
    return "base64_url_with_padding";
12075
0
  case base64_default_accept_garbage:
12076
0
    return "base64_default_accept_garbage";
12077
0
  case base64_url_accept_garbage:
12078
0
    return "base64_url_accept_garbage";
12079
0
  case base64_default_or_url:
12080
0
    return "base64_default_or_url";
12081
0
  case base64_default_or_url_accept_garbage:
12082
0
    return "base64_default_or_url_accept_garbage";
12083
0
  }
12084
0
  return "<unknown>";
12085
0
}
12086
12087
0
inline std::string_view to_string(last_chunk_handling_options options) {
12088
0
  switch (options) {
12089
0
  case loose:
12090
0
    return "loose";
12091
0
  case strict:
12092
0
    return "strict";
12093
0
  case stop_before_partial:
12094
0
    return "stop_before_partial";
12095
0
  case only_full_chunks:
12096
0
    return "only_full_chunks";
12097
0
  }
12098
0
  return "<unknown>";
12099
0
}
12100
12101
/**
12102
 * Provide the maximal binary length in bytes given the base64 input.
12103
 * As long as the input does not contain ignorable characters (e.g., ASCII
12104
 * spaces or linefeed characters), the result is exact. In particular, the
12105
 * function checks for padding characters.
12106
 *
12107
 * The function is fast (constant time). It checks up to two characters at
12108
 * the end of the string. The input is not otherwise validated or read.
12109
 *
12110
 * @param input         the base64 input to process
12111
 * @param length        the length of the base64 input in bytes
12112
 * @return maximum number of binary bytes
12113
 */
12114
simdutf_warn_unused size_t
12115
maximal_binary_length_from_base64(const char *input, size_t length) noexcept;
12116
  #if SIMDUTF_SPAN
12117
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12118
maximal_binary_length_from_base64(
12119
    const detail::input_span_of_byte_like auto &input) noexcept {
12120
    #if SIMDUTF_CPLUSPLUS23
12121
  if consteval {
12122
    return scalar::base64::maximal_binary_length_from_base64(
12123
        detail::constexpr_cast_ptr<uint8_t>(input.data()), input.size());
12124
  } else
12125
    #endif
12126
  {
12127
    return maximal_binary_length_from_base64(
12128
        reinterpret_cast<const char *>(input.data()), input.size());
12129
  }
12130
}
12131
  #endif // SIMDUTF_SPAN
12132
12133
/**
12134
 * Provide the maximal binary length in bytes given the base64 input.
12135
 * As long as the input does not contain ignorable characters (e.g., ASCII
12136
 * spaces or linefeed characters), the result is exact. In particular, the
12137
 * function checks for padding characters.
12138
 *
12139
 * The function is fast (constant time). It checks up to two characters at
12140
 * the end of the string. The input is not otherwise validated or read.
12141
 *
12142
 * @param input         the base64 input to process, in ASCII stored as 16-bit
12143
 * units
12144
 * @param length        the length of the base64 input in 16-bit units
12145
 * @return maximal number of binary bytes
12146
 */
12147
simdutf_warn_unused size_t maximal_binary_length_from_base64(
12148
    const char16_t *input, size_t length) noexcept;
12149
  #if SIMDUTF_SPAN
12150
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12151
0
maximal_binary_length_from_base64(std::span<const char16_t> input) noexcept {
12152
0
    #if SIMDUTF_CPLUSPLUS23
12153
0
  if consteval {
12154
0
    return scalar::base64::maximal_binary_length_from_base64(input.data(),
12155
0
                                                             input.size());
12156
0
  } else
12157
0
    #endif
12158
0
  {
12159
0
    return maximal_binary_length_from_base64(input.data(), input.size());
12160
0
  }
12161
0
}
12162
  #endif // SIMDUTF_SPAN
12163
12164
/**
12165
 * Compute the binary length from a base64 input.
12166
 * This function is useful for base64 inputs that may contain ASCII whitespaces
12167
 * (such as line breaks). For such inputs, the result is exact, and for any
12168
 * inputs the result can be used to size the output buffer passed to
12169
 * `base64_to_binary`.
12170
 *
12171
 * The function ignores whitespace and does not require padding characters
12172
 * ('=').
12173
 *
12174
 * @param input         the base64 input to process
12175
 * @param length        the length of the base64 input in bytes
12176
 * @return number of binary bytes
12177
 */
12178
simdutf_warn_unused size_t binary_length_from_base64(const char *input,
12179
                                                     size_t length) noexcept;
12180
  #if SIMDUTF_SPAN
12181
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12182
binary_length_from_base64(
12183
    const detail::input_span_of_byte_like auto &input) noexcept {
12184
    #if SIMDUTF_CPLUSPLUS23
12185
  if consteval {
12186
    return scalar::base64::binary_length_from_base64(input.data(),
12187
                                                     input.size());
12188
  } else
12189
    #endif
12190
  {
12191
    return binary_length_from_base64(
12192
        reinterpret_cast<const char *>(input.data()), input.size());
12193
  }
12194
}
12195
  #endif // SIMDUTF_SPAN
12196
12197
/**
12198
 * Compute the binary length from a base64 input.
12199
 * This function is useful for base64 inputs that may contain ASCII whitespaces
12200
 * (such as line breaks). For such inputs, the result is exact, and for any
12201
 * inputs the result can be used to size the output buffer passed to
12202
 * `base64_to_binary`.
12203
 *
12204
 * The function ignores whitespace and does not require padding characters
12205
 * ('=').
12206
 *
12207
 * @param input         the base64 input to process, in ASCII stored as 16-bit
12208
 * units
12209
 * @param length        the length of the base64 input in 16-bit units
12210
 * @return number of binary bytes
12211
 */
12212
simdutf_warn_unused size_t binary_length_from_base64(const char16_t *input,
12213
                                                     size_t length) noexcept;
12214
  #if SIMDUTF_SPAN
12215
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12216
0
binary_length_from_base64(std::span<const char16_t> input) noexcept {
12217
0
    #if SIMDUTF_CPLUSPLUS23
12218
0
  if consteval {
12219
0
    return scalar::base64::binary_length_from_base64(input.data(),
12220
0
                                                     input.size());
12221
0
  } else
12222
0
    #endif
12223
0
  {
12224
0
    return binary_length_from_base64(input.data(), input.size());
12225
0
  }
12226
0
}
12227
  #endif // SIMDUTF_SPAN
12228
12229
/**
12230
 * Convert a base64 input to a binary output.
12231
 *
12232
 * This function follows the WHATWG forgiving-base64 format, which means that it
12233
 * will ignore any ASCII spaces in the input. You may provide a padded input
12234
 * (with one or two equal signs at the end) or an unpadded input (without any
12235
 * equal signs at the end).
12236
 *
12237
 * See https://infra.spec.whatwg.org/#forgiving-base64-decode
12238
 *
12239
 * This function will fail in case of invalid input. When last_chunk_options =
12240
 * loose, there are two possible reasons for failure: the input contains a
12241
 * number of base64 characters that when divided by 4, leaves a single remainder
12242
 * character (BASE64_INPUT_REMAINDER), or the input contains a character that is
12243
 * not a valid base64 character (INVALID_BASE64_CHARACTER).
12244
 *
12245
 * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the
12246
 * input where the invalid character was found. When the error is
12247
 * BASE64_INPUT_REMAINDER, then r.count contains the number of bytes decoded.
12248
 *
12249
 * The default option (simdutf::base64_default) expects the characters `+` and
12250
 * `/` as part of its alphabet. The URL option (simdutf::base64_url) expects the
12251
 * characters `-` and `_` as part of its alphabet.
12252
 *
12253
 * The padding (`=`) is validated if present. There may be at most two padding
12254
 * characters at the end of the input. If there are any padding characters, the
12255
 * total number of characters (excluding spaces but including padding
12256
 * characters) must be divisible by four.
12257
 *
12258
 * You should call this function with a buffer that is at least
12259
 * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
12260
 * provide that much space, the function may cause a buffer overflow.
12261
 *
12262
 * Advanced users may want to tailor how the last chunk is handled. By default,
12263
 * we use a loose (forgiving) approach but we also support a strict approach
12264
 * as well as a stop_before_partial approach, as per the following proposal:
12265
 *
12266
 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
12267
 *
12268
 * @param input         the base64 string to process
12269
 * @param length        the length of the string in bytes
12270
 * @param output        the pointer to a buffer that can hold the conversion
12271
 * result (should be at least maximal_binary_length_from_base64(input, length)
12272
 * bytes long).
12273
 * @param options       the base64 options to use, usually base64_default or
12274
 * base64_url, and base64_default by default.
12275
 * @param last_chunk_options the last chunk handling options,
12276
 * last_chunk_handling_options::loose by default
12277
 * but can also be last_chunk_handling_options::strict or
12278
 * last_chunk_handling_options::stop_before_partial.
12279
 * @return a result pair struct (of type simdutf::result containing the two
12280
 * fields error and count) with an error code and either position of the error
12281
 * (in the input in bytes) if any, or the number of bytes written if successful.
12282
 */
12283
simdutf_warn_unused result base64_to_binary(
12284
    const char *input, size_t length, char *output,
12285
    base64_options options = base64_default,
12286
    last_chunk_handling_options last_chunk_options = loose) noexcept;
12287
  #if SIMDUTF_SPAN
12288
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
12289
base64_to_binary(
12290
    const detail::input_span_of_byte_like auto &input,
12291
    detail::output_span_of_byte_like auto &&binary_output,
12292
    base64_options options = base64_default,
12293
    last_chunk_handling_options last_chunk_options = loose) noexcept {
12294
    #if SIMDUTF_CPLUSPLUS23
12295
  if consteval {
12296
    return scalar::base64::base64_to_binary_details_impl(
12297
        input.data(), input.size(), binary_output.data(), options,
12298
        last_chunk_options);
12299
  } else
12300
    #endif
12301
  {
12302
    return base64_to_binary(reinterpret_cast<const char *>(input.data()),
12303
                            input.size(),
12304
                            reinterpret_cast<char *>(binary_output.data()),
12305
                            options, last_chunk_options);
12306
  }
12307
}
12308
  #endif // SIMDUTF_SPAN
12309
12310
/**
12311
 * Provide the base64 length in bytes given the length of a binary input.
12312
 *
12313
 * @param length        the length of the input in bytes
12314
 * @param options       the base64 options to use (default: base64_default)
12315
 * @return number of base64 bytes
12316
 */
12317
inline simdutf_warn_unused simdutf_constexpr23 size_t base64_length_from_binary(
12318
0
    size_t length, base64_options options = base64_default) noexcept {
12319
0
  return scalar::base64::base64_length_from_binary(length, options);
12320
0
}
12321
12322
/**
12323
 * Provide the base64 length in bytes given the length of a binary input,
12324
 * taking into account line breaks.
12325
 *
12326
 * @param length        the length of the input in bytes
12327
 * @param options       the base64 options to use (default: base64_default)
12328
 * @param line_length   the length of lines, must be at least 4 (otherwise it is
12329
 * interpreted as 4),
12330
 * @return number of base64 bytes
12331
 */
12332
inline simdutf_warn_unused simdutf_constexpr23 size_t
12333
base64_length_from_binary_with_lines(
12334
    size_t length, base64_options options = base64_default,
12335
0
    size_t line_length = default_line_length) noexcept {
12336
0
  return scalar::base64::base64_length_from_binary_with_lines(length, options,
12337
0
                                                              line_length);
12338
0
}
12339
12340
/**
12341
 * Convert a binary input to a base64 output.
12342
 *
12343
 * The default option (simdutf::base64_default) uses the characters `+` and `/`
12344
 * as part of its alphabet. Further, it adds padding (`=`) at the end of the
12345
 * output to ensure that the output length is a multiple of four.
12346
 *
12347
 * The URL option (simdutf::base64_url) uses the characters `-` and `_` as part
12348
 * of its alphabet. No padding is added at the end of the output.
12349
 *
12350
 * This function always succeeds.
12351
 *
12352
 * @param input         the binary to process
12353
 * @param length        the length of the input in bytes
12354
 * @param output        the pointer to a buffer that can hold the conversion
12355
 * result (should be at least base64_length_from_binary(length) bytes long)
12356
 * @param options       the base64 options to use, can be base64_default or
12357
 * base64_url, is base64_default by default.
12358
 * @return number of written bytes, will be equal to
12359
 * base64_length_from_binary(length, options)
12360
 */
12361
size_t binary_to_base64(const char *input, size_t length, char *output,
12362
                        base64_options options = base64_default) noexcept;
12363
  #if SIMDUTF_SPAN
12364
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12365
binary_to_base64(const detail::input_span_of_byte_like auto &input,
12366
                 detail::output_span_of_byte_like auto &&binary_output,
12367
                 base64_options options = base64_default) noexcept {
12368
    #if SIMDUTF_CPLUSPLUS23
12369
  if consteval {
12370
    return scalar::base64::tail_encode_base64(
12371
        binary_output.data(), input.data(), input.size(), options);
12372
  } else
12373
    #endif
12374
  {
12375
    return binary_to_base64(
12376
        reinterpret_cast<const char *>(input.data()), input.size(),
12377
        reinterpret_cast<char *>(binary_output.data()), options);
12378
  }
12379
}
12380
  #endif // SIMDUTF_SPAN
12381
12382
/**
12383
 * Convert a binary input to a base64 output with line breaks.
12384
 *
12385
 * The default option (simdutf::base64_default) uses the characters `+` and `/`
12386
 * as part of its alphabet. Further, it adds padding (`=`) at the end of the
12387
 * output to ensure that the output length is a multiple of four.
12388
 *
12389
 * The URL option (simdutf::base64_url) uses the characters `-` and `_` as part
12390
 * of its alphabet. No padding is added at the end of the output.
12391
 *
12392
 * This function always succeeds.
12393
 *
12394
 * @param input         the binary to process
12395
 * @param length        the length of the input in bytes
12396
 * @param output        the pointer to a buffer that can hold the conversion
12397
 * result (should be at least base64_length_from_binary_with_lines(length,
12398
 * options, line_length) bytes long)
12399
 * @param line_length   the length of lines, must be at least 4 (otherwise it is
12400
 * interpreted as 4),
12401
 * @param options       the base64 options to use, can be base64_default or
12402
 * base64_url, is base64_default by default.
12403
 * @return number of written bytes, will be equal to
12404
 * base64_length_from_binary_with_lines(length, options)
12405
 */
12406
size_t
12407
binary_to_base64_with_lines(const char *input, size_t length, char *output,
12408
                            size_t line_length = simdutf::default_line_length,
12409
                            base64_options options = base64_default) noexcept;
12410
  #if SIMDUTF_SPAN
12411
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 size_t
12412
binary_to_base64_with_lines(
12413
    const detail::input_span_of_byte_like auto &input,
12414
    detail::output_span_of_byte_like auto &&binary_output,
12415
    size_t line_length = simdutf::default_line_length,
12416
    base64_options options = base64_default) noexcept {
12417
    #if SIMDUTF_CPLUSPLUS23
12418
  if consteval {
12419
    return scalar::base64::tail_encode_base64_impl<true>(
12420
        binary_output.data(), input.data(), input.size(), options, line_length);
12421
  } else
12422
    #endif
12423
  {
12424
    return binary_to_base64_with_lines(
12425
        reinterpret_cast<const char *>(input.data()), input.size(),
12426
        reinterpret_cast<char *>(binary_output.data()), line_length, options);
12427
  }
12428
}
12429
  #endif // SIMDUTF_SPAN
12430
12431
  #if SIMDUTF_ATOMIC_REF
12432
/**
12433
 * Convert a binary input to a base64 output, using atomic accesses.
12434
 * This function comes with a potentially significant performance
12435
 * penalty, but it may be useful in some cases where the input
12436
 * buffers are shared between threads, to avoid undefined
12437
 * behavior in case of data races.
12438
 *
12439
 * The function is for advanced users. Its main use case is when
12440
 * to silence sanitizer warnings. We have no documented use case
12441
 * where this function is actually necessary in terms of practical correctness.
12442
 *
12443
 * This function is only available when simdutf is compiled with
12444
 * C++20 support and __cpp_lib_atomic_ref >= 201806L. You may check
12445
 * the availability of this function by checking the macro
12446
 * SIMDUTF_ATOMIC_REF.
12447
 *
12448
 * The default option (simdutf::base64_default) uses the characters `+` and `/`
12449
 * as part of its alphabet. Further, it adds padding (`=`) at the end of the
12450
 * output to ensure that the output length is a multiple of four.
12451
 *
12452
 * The URL option (simdutf::base64_url) uses the characters `-` and `_` as part
12453
 * of its alphabet. No padding is added at the end of the output.
12454
 *
12455
 * This function always succeeds.
12456
 *
12457
 * This function is considered experimental. It is not tested by default
12458
 * (see the CMake option SIMDUTF_ATOMIC_BASE64_TESTS) nor is it fuzz tested.
12459
 * It is not documented in the public API documentation (README). It is
12460
 * offered on a best effort basis. We rely on the community for further
12461
 * testing and feedback.
12462
 *
12463
 * @brief atomic_binary_to_base64
12464
 * @param input         the binary to process
12465
 * @param length        the length of the input in bytes
12466
 * @param output        the pointer to a buffer that can hold the conversion
12467
 * result (should be at least base64_length_from_binary(length) bytes long)
12468
 * @param options       the base64 options to use, can be base64_default or
12469
 * base64_url, is base64_default by default.
12470
 * @return number of written bytes, will be equal to
12471
 * base64_length_from_binary(length, options)
12472
 */
12473
size_t
12474
atomic_binary_to_base64(const char *input, size_t length, char *output,
12475
                        base64_options options = base64_default) noexcept;
12476
    #if SIMDUTF_SPAN
12477
simdutf_really_inline simdutf_warn_unused size_t
12478
atomic_binary_to_base64(const detail::input_span_of_byte_like auto &input,
12479
                        detail::output_span_of_byte_like auto &&binary_output,
12480
                        base64_options options = base64_default) noexcept {
12481
  return atomic_binary_to_base64(
12482
      reinterpret_cast<const char *>(input.data()), input.size(),
12483
      reinterpret_cast<char *>(binary_output.data()), options);
12484
}
12485
    #endif // SIMDUTF_SPAN
12486
  #endif   // SIMDUTF_ATOMIC_REF
12487
12488
/**
12489
 * Convert a base64 input to a binary output.
12490
 *
12491
 * This function follows the WHATWG forgiving-base64 format, which means that it
12492
 * will ignore any ASCII spaces in the input. You may provide a padded input
12493
 * (with one or two equal signs at the end) or an unpadded input (without any
12494
 * equal signs at the end).
12495
 *
12496
 * See https://infra.spec.whatwg.org/#forgiving-base64-decode
12497
 *
12498
 * This function will fail in case of invalid input. When last_chunk_options =
12499
 * loose, there are two possible reasons for failure: the input contains a
12500
 * number of base64 characters that when divided by 4, leaves a single remainder
12501
 * character (BASE64_INPUT_REMAINDER), or the input contains a character that is
12502
 * not a valid base64 character (INVALID_BASE64_CHARACTER).
12503
 *
12504
 * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the
12505
 * input where the invalid character was found. When the error is
12506
 * BASE64_INPUT_REMAINDER, then r.count contains the number of bytes decoded.
12507
 *
12508
 * The default option (simdutf::base64_default) expects the characters `+` and
12509
 * `/` as part of its alphabet. The URL option (simdutf::base64_url) expects the
12510
 * characters `-` and `_` as part of its alphabet.
12511
 *
12512
 * The padding (`=`) is validated if present. There may be at most two padding
12513
 * characters at the end of the input. If there are any padding characters, the
12514
 * total number of characters (excluding spaces but including padding
12515
 * characters) must be divisible by four.
12516
 *
12517
 * You should call this function with a buffer that is at least
12518
 * maximal_binary_length_from_base64(input, length) bytes long. If you fail
12519
 * to provide that much space, the function may cause a buffer overflow.
12520
 *
12521
 * Advanced users may want to tailor how the last chunk is handled. By default,
12522
 * we use a loose (forgiving) approach but we also support a strict approach
12523
 * as well as a stop_before_partial approach, as per the following proposal:
12524
 *
12525
 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
12526
 *
12527
 * @param input         the base64 string to process, in ASCII stored as 16-bit
12528
 * units
12529
 * @param length        the length of the string in 16-bit units
12530
 * @param output        the pointer to a buffer that can hold the conversion
12531
 * result (should be at least maximal_binary_length_from_base64(input, length)
12532
 * bytes long).
12533
 * @param options       the base64 options to use, can be base64_default or
12534
 * base64_url, is base64_default by default.
12535
 * @param last_chunk_options the last chunk handling options,
12536
 * last_chunk_handling_options::loose by default
12537
 * but can also be last_chunk_handling_options::strict or
12538
 * last_chunk_handling_options::stop_before_partial.
12539
 * @return a result pair struct (of type simdutf::result containing the two
12540
 * fields error and count) with an error code and position of the
12541
 * INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number
12542
 * of bytes written if successful.
12543
 */
12544
simdutf_warn_unused result
12545
base64_to_binary(const char16_t *input, size_t length, char *output,
12546
                 base64_options options = base64_default,
12547
                 last_chunk_handling_options last_chunk_options =
12548
                     last_chunk_handling_options::loose) noexcept;
12549
  #if SIMDUTF_SPAN
12550
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 result
12551
base64_to_binary(
12552
    std::span<const char16_t> input,
12553
    detail::output_span_of_byte_like auto &&binary_output,
12554
    base64_options options = base64_default,
12555
    last_chunk_handling_options last_chunk_options = loose) noexcept {
12556
    #if SIMDUTF_CPLUSPLUS23
12557
  if consteval {
12558
    return scalar::base64::base64_to_binary_details_impl(
12559
        input.data(), input.size(), binary_output.data(), options,
12560
        last_chunk_options);
12561
  } else
12562
    #endif
12563
  {
12564
    return base64_to_binary(input.data(), input.size(),
12565
                            reinterpret_cast<char *>(binary_output.data()),
12566
                            options, last_chunk_options);
12567
  }
12568
}
12569
  #endif // SIMDUTF_SPAN
12570
12571
/**
12572
 * Convert a base64 input to a binary output while returning more details
12573
 * than base64_to_binary.
12574
 *
12575
 * This function follows the WHATWG forgiving-base64 format, which means that it
12576
 * will ignore any ASCII spaces in the input. You may provide a padded input
12577
 * (with one or two equal signs at the end) or an unpadded input (without any
12578
 * equal signs at the end).
12579
 *
12580
 * See https://infra.spec.whatwg.org/#forgiving-base64-decode
12581
 *
12582
 * Unlike base64_to_binary, this function returns a full_result with both
12583
 * input_count and output_count, so you always know how much input was consumed
12584
 * and how much output was written. There are three cases where the input may
12585
 * not be fully consumed:
12586
 *
12587
 * 1. stop_before_partial: When last_chunk_options is set to
12588
 *    stop_before_partial, any incomplete 4-character group at the end of the
12589
 *    input is left unconsumed. This is useful for streaming/chunked decoding
12590
 *    where you can carry over the unconsumed input to the next chunk.
12591
 *
12592
 * 2. INVALID_BASE64_CHARACTER: The input contains a character that is not a
12593
 *    valid base64 character. In this case, input_count indicates where the
12594
 *    invalid character was found.
12595
 *
12596
 * 3. BASE64_INPUT_REMAINDER: When last_chunk_options is loose, the input
12597
 *    contains a number of base64 characters that, when divided by 4, leaves
12598
 *    a single remainder character (which cannot encode any bytes).
12599
 *
12600
 * You should call this function with a buffer that is at least
12601
 * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
12602
 * provide that much space, the function may cause a buffer overflow.
12603
 *
12604
 * @param input         the base64 string to process
12605
 * @param length        the length of the string in bytes
12606
 * @param output        the pointer to a buffer that can hold the conversion
12607
 * result (should be at least maximal_binary_length_from_base64(input, length)
12608
 * bytes long).
12609
 * @param options       the base64 options to use, can be base64_default or
12610
 * base64_url, is base64_default by default.
12611
 * @param last_chunk_options the last chunk handling options,
12612
 * last_chunk_handling_options::loose by default
12613
 * but can also be last_chunk_handling_options::strict or
12614
 * last_chunk_handling_options::stop_before_partial.
12615
 * @return a full_result struct (of type simdutf::full_result containing the
12616
 * three fields error, input_count and output_count).
12617
 */
12618
simdutf_warn_unused full_result
12619
base64_to_binary_details(const char *input, size_t length, char *output,
12620
                         base64_options options = base64_default,
12621
                         last_chunk_handling_options last_chunk_options =
12622
                             last_chunk_handling_options::loose) noexcept;
12623
  #if SIMDUTF_SPAN
12624
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result
12625
base64_to_binary_details(
12626
    const detail::input_span_of_byte_like auto &input,
12627
    detail::output_span_of_byte_like auto &&binary_output,
12628
    base64_options options = base64_default,
12629
    last_chunk_handling_options last_chunk_options = loose) noexcept {
12630
    #if SIMDUTF_CPLUSPLUS23
12631
  if consteval {
12632
    return scalar::base64::base64_to_binary_details_impl(
12633
        input.data(), input.size(), binary_output.data(), options,
12634
        last_chunk_options);
12635
  } else
12636
    #endif
12637
  {
12638
    return base64_to_binary_details(
12639
        reinterpret_cast<const char *>(input.data()), input.size(),
12640
        reinterpret_cast<char *>(binary_output.data()), options,
12641
        last_chunk_options);
12642
  }
12643
}
12644
  #endif // SIMDUTF_SPAN
12645
12646
/**
12647
 * Convert a base64 input to a binary output while returning more details
12648
 * than base64_to_binary.
12649
 *
12650
 * This function follows the WHATWG forgiving-base64 format, which means that it
12651
 * will ignore any ASCII spaces in the input. You may provide a padded input
12652
 * (with one or two equal signs at the end) or an unpadded input (without any
12653
 * equal signs at the end).
12654
 *
12655
 * See https://infra.spec.whatwg.org/#forgiving-base64-decode
12656
 *
12657
 * Unlike base64_to_binary, this function returns a full_result with both
12658
 * input_count and output_count, so you always know how much input was consumed
12659
 * and how much output was written. There are three cases where the input may
12660
 * not be fully consumed:
12661
 *
12662
 * 1. stop_before_partial: When last_chunk_options is set to
12663
 *    stop_before_partial, any incomplete 4-character group at the end of the
12664
 *    input is left unconsumed. This is useful for streaming/chunked decoding
12665
 *    where you can carry over the unconsumed input to the next chunk.
12666
 *
12667
 * 2. INVALID_BASE64_CHARACTER: The input contains a character that is not a
12668
 *    valid base64 character. In this case, input_count indicates where the
12669
 *    invalid character was found.
12670
 *
12671
 * 3. BASE64_INPUT_REMAINDER: When last_chunk_options is loose, the input
12672
 *    contains a number of base64 characters that, when divided by 4, leaves
12673
 *    a single remainder character (which cannot encode any bytes).
12674
 *
12675
 * You should call this function with a buffer that is at least
12676
 * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
12677
 * provide that much space, the function may cause a buffer overflow.
12678
 *
12679
 * @param input         the base64 string to process, in ASCII stored as 16-bit
12680
 * units
12681
 * @param length        the length of the string in 16-bit units
12682
 * @param output        the pointer to a buffer that can hold the conversion
12683
 * result (should be at least maximal_binary_length_from_base64(input, length)
12684
 * bytes long).
12685
 * @param options       the base64 options to use, can be base64_default or
12686
 * base64_url, is base64_default by default.
12687
 * @param last_chunk_options the last chunk handling options,
12688
 * last_chunk_handling_options::loose by default
12689
 * but can also be last_chunk_handling_options::strict or
12690
 * last_chunk_handling_options::stop_before_partial.
12691
 * @return a full_result struct (of type simdutf::full_result containing the
12692
 * three fields error, input_count and output_count).
12693
 */
12694
simdutf_warn_unused full_result
12695
base64_to_binary_details(const char16_t *input, size_t length, char *output,
12696
                         base64_options options = base64_default,
12697
                         last_chunk_handling_options last_chunk_options =
12698
                             last_chunk_handling_options::loose) noexcept;
12699
  #if SIMDUTF_SPAN
12700
simdutf_really_inline simdutf_warn_unused simdutf_constexpr23 full_result
12701
base64_to_binary_details(
12702
    std::span<const char16_t> input,
12703
    detail::output_span_of_byte_like auto &&binary_output,
12704
    base64_options options = base64_default,
12705
    last_chunk_handling_options last_chunk_options = loose) noexcept {
12706
    #if SIMDUTF_CPLUSPLUS23
12707
  if consteval {
12708
    return scalar::base64::base64_to_binary_details_impl(
12709
        input.data(), input.size(), binary_output.data(), options,
12710
        last_chunk_options);
12711
  } else
12712
    #endif
12713
  {
12714
    return base64_to_binary_details(
12715
        input.data(), input.size(),
12716
        reinterpret_cast<char *>(binary_output.data()), options,
12717
        last_chunk_options);
12718
  }
12719
}
12720
  #endif // SIMDUTF_SPAN
12721
12722
/**
12723
 * Check if a character is an ignorable base64 character.
12724
 * Checking a large input, character by character, is not computationally
12725
 * efficient.
12726
 *
12727
 * @param input         the character to check
12728
 * @param options       the base64 options to use, is base64_default by default.
12729
 * @return true if the character is an ignorable base64 character, false
12730
 * otherwise.
12731
 */
12732
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12733
0
base64_ignorable(char input, base64_options options = base64_default) noexcept {
12734
0
  return scalar::base64::is_ignorable(input, options);
12735
0
}
12736
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12737
base64_ignorable(char16_t input,
12738
0
                 base64_options options = base64_default) noexcept {
12739
0
  return scalar::base64::is_ignorable(input, options);
12740
0
}
12741
12742
/**
12743
 * Check if a character is a valid base64 character.
12744
 * Checking a large input, character by character, is not computationally
12745
 * efficient.
12746
 * Note that padding characters are not considered valid base64 characters in
12747
 * this context, nor are spaces.
12748
 *
12749
 * @param input         the character to check
12750
 * @param options       the base64 options to use, is base64_default by default.
12751
 * @return true if the character is a base64 character, false otherwise.
12752
 */
12753
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12754
0
base64_valid(char input, base64_options options = base64_default) noexcept {
12755
0
  return scalar::base64::is_base64(input, options);
12756
0
}
12757
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12758
0
base64_valid(char16_t input, base64_options options = base64_default) noexcept {
12759
0
  return scalar::base64::is_base64(input, options);
12760
0
}
12761
12762
/**
12763
 * Check if a character is a valid base64 character or the padding character
12764
 * ('='). Checking a large input, character by character, is not computationally
12765
 * efficient.
12766
 *
12767
 * @param input         the character to check
12768
 * @param options       the base64 options to use, is base64_default by default.
12769
 * @return true if the character is a base64 character, false otherwise.
12770
 */
12771
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12772
base64_valid_or_padding(char input,
12773
0
                        base64_options options = base64_default) noexcept {
12774
0
  return scalar::base64::is_base64_or_padding(input, options);
12775
0
}
12776
simdutf_warn_unused simdutf_really_inline simdutf_constexpr23 bool
12777
base64_valid_or_padding(char16_t input,
12778
0
                        base64_options options = base64_default) noexcept {
12779
0
  return scalar::base64::is_base64_or_padding(input, options);
12780
0
}
12781
12782
/**
12783
 * Convert a base64 input to a binary output.
12784
 *
12785
 * This function follows the WHATWG forgiving-base64 format, which means that it
12786
 * will ignore any ASCII spaces in the input. You may provide a padded input
12787
 * (with one or two equal signs at the end) or an unpadded input (without any
12788
 * equal signs at the end).
12789
 *
12790
 * See https://infra.spec.whatwg.org/#forgiving-base64-decode
12791
 *
12792
 * This function will fail in case of invalid input. When last_chunk_options =
12793
 * loose, there are three possible reasons for failure: the input contains a
12794
 * number of base64 characters that when divided by 4, leaves a single remainder
12795
 * character (BASE64_INPUT_REMAINDER), the input contains a character that is
12796
 * not a valid base64 character (INVALID_BASE64_CHARACTER), or the output buffer
12797
 * is too small (OUTPUT_BUFFER_TOO_SMALL).
12798
 *
12799
 * When OUTPUT_BUFFER_TOO_SMALL, we return both the number of bytes written
12800
 * and the number of units processed, see description of the parameters and
12801
 * returned value.
12802
 *
12803
 * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the
12804
 * input where the invalid character was found. When the error is
12805
 * BASE64_INPUT_REMAINDER, then r.count contains the number of bytes decoded.
12806
 *
12807
 * The default option (simdutf::base64_default) expects the characters `+` and
12808
 * `/` as part of its alphabet. The URL option (simdutf::base64_url) expects the
12809
 * characters `-` and `_` as part of its alphabet.
12810
 *
12811
 * The padding (`=`) is validated if present. There may be at most two padding
12812
 * characters at the end of the input. If there are any padding characters, the
12813
 * total number of characters (excluding spaces but including padding
12814
 * characters) must be divisible by four.
12815
 *
12816
 * The INVALID_BASE64_CHARACTER cases are considered fatal and you are expected
12817
 * to discard the output unless the parameter decode_up_to_bad_char is set to
12818
 * true. In that case, the function will decode up to the first invalid
12819
 * character. Extra padding characters ('=') are considered invalid characters.
12820
 *
12821
 * Advanced users may want to tailor how the last chunk is handled. By default,
12822
 * we use a loose (forgiving) approach but we also support a strict approach
12823
 * as well as a stop_before_partial approach, as per the following proposal:
12824
 *
12825
 * https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
12826
 *
12827
 * The base64_to_binary_safe function has negligible overhead compared with
12828
 * base64_to_binary in the absence of ignorable characters; however, on short
12829
 * inputs containing ignorable characters, it can be up to three times slower.
12830
 *
12831
 * @param input         the base64 string to process, in ASCII stored as 8-bit
12832
 * or 16-bit units
12833
 * @param length        the length of the string in 8-bit or 16-bit units.
12834
 * @param output        the pointer to a buffer that can hold the conversion
12835
 * result.
12836
 * @param outlen        the number of bytes that can be written in the output
12837
 * buffer. Upon return, it is modified to reflect how many bytes were written.
12838
 * @param options       the base64 options to use, can be base64_default or
12839
 * base64_url, is base64_default by default.
12840
 * @param last_chunk_options the last chunk handling options,
12841
 * last_chunk_handling_options::loose by default
12842
 * but can also be last_chunk_handling_options::strict or
12843
 * last_chunk_handling_options::stop_before_partial.
12844
 * @param decode_up_to_bad_char if true, the function will decode up to the
12845
 * first invalid character. By default (false), it is assumed that the output
12846
 * buffer is to be discarded. When there are multiple errors in the input,
12847
 * using decode_up_to_bad_char might trigger a different error.
12848
 * @return a result pair struct (of type simdutf::result containing the two
12849
 * fields error and count) with an error code and position of the
12850
 * INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number
12851
 * of units processed if successful.
12852
 */
12853
simdutf_warn_unused result
12854
base64_to_binary_safe(const char *input, size_t length, char *output,
12855
                      size_t &outlen, base64_options options = base64_default,
12856
                      last_chunk_handling_options last_chunk_options =
12857
                          last_chunk_handling_options::loose,
12858
                      bool decode_up_to_bad_char = false) noexcept;
12859
// the span overload has moved to the bottom of the file
12860
12861
simdutf_warn_unused result
12862
base64_to_binary_safe(const char16_t *input, size_t length, char *output,
12863
                      size_t &outlen, base64_options options = base64_default,
12864
                      last_chunk_handling_options last_chunk_options =
12865
                          last_chunk_handling_options::loose,
12866
                      bool decode_up_to_bad_char = false) noexcept;
12867
  // span overload moved to bottom of file
12868
12869
  #if SIMDUTF_ATOMIC_REF
12870
/**
12871
 * Convert a base64 input to a binary output with a size limit and using atomic
12872
 * operations.
12873
 *
12874
 * Like `base64_to_binary_safe` but using atomic operations, this function is
12875
 * thread-safe for concurrent memory access, allowing the output
12876
 * buffers to be shared between threads without undefined behavior in case of
12877
 * data races.
12878
 *
12879
 * This function comes with a potentially significant performance penalty, but
12880
 * is useful when thread safety is needed during base64 decoding.
12881
 *
12882
 * This function is only available when simdutf is compiled with
12883
 * C++20 support and __cpp_lib_atomic_ref >= 201806L. You may check
12884
 * the availability of this function by checking the macro
12885
 * SIMDUTF_ATOMIC_REF.
12886
 *
12887
 * This function is considered experimental. It is not tested by default
12888
 * (see the CMake option SIMDUTF_ATOMIC_BASE64_TESTS) nor is it fuzz tested.
12889
 * It is not documented in the public API documentation (README). It is
12890
 * offered on a best effort basis. We rely on the community for further
12891
 * testing and feedback.
12892
 *
12893
 * @param input         the base64 input to decode
12894
 * @param length        the length of the input in bytes
12895
 * @param output        the pointer to buffer that can hold the conversion
12896
 * result
12897
 * @param outlen        the number of bytes that can be written in the output
12898
 * buffer. Upon return, it is modified to reflect how many bytes were written.
12899
 * @param options       the base64 options to use (default, url, etc.)
12900
 * @param last_chunk_options the last chunk handling options (loose, strict,
12901
 * stop_before_partial)
12902
 * @param decode_up_to_bad_char if true, the function will decode up to the
12903
 * first invalid character. By default (false), it is assumed that the output
12904
 * buffer is to be discarded. When there are multiple errors in the input,
12905
 * using decode_up_to_bad_char might trigger a different error.
12906
 * @return a result struct with an error code and count indicating error
12907
 * position or success
12908
 */
12909
simdutf_warn_unused result atomic_base64_to_binary_safe(
12910
    const char *input, size_t length, char *output, size_t &outlen,
12911
    base64_options options = base64_default,
12912
    last_chunk_handling_options last_chunk_options =
12913
        last_chunk_handling_options::loose,
12914
    bool decode_up_to_bad_char = false) noexcept;
12915
simdutf_warn_unused result atomic_base64_to_binary_safe(
12916
    const char16_t *input, size_t length, char *output, size_t &outlen,
12917
    base64_options options = base64_default,
12918
    last_chunk_handling_options last_chunk_options = loose,
12919
    bool decode_up_to_bad_char = false) noexcept;
12920
    #if SIMDUTF_SPAN
12921
/**
12922
 * @brief span overload
12923
 * @return a tuple of result and outlen
12924
 */
12925
simdutf_really_inline simdutf_warn_unused std::tuple<result, std::size_t>
12926
atomic_base64_to_binary_safe(
12927
    const detail::input_span_of_byte_like auto &binary_input,
12928
    detail::output_span_of_byte_like auto &&output,
12929
    base64_options options = base64_default,
12930
    last_chunk_handling_options last_chunk_options =
12931
        last_chunk_handling_options::loose,
12932
    bool decode_up_to_bad_char = false) noexcept {
12933
  size_t outlen = output.size();
12934
  auto ret = atomic_base64_to_binary_safe(
12935
      reinterpret_cast<const char *>(binary_input.data()), binary_input.size(),
12936
      reinterpret_cast<char *>(output.data()), outlen, options,
12937
      last_chunk_options, decode_up_to_bad_char);
12938
  return {ret, outlen};
12939
}
12940
/**
12941
 * @brief span overload
12942
 * @return a tuple of result and outlen
12943
 */
12944
simdutf_warn_unused std::tuple<result, std::size_t>
12945
atomic_base64_to_binary_safe(
12946
    std::span<const char16_t> base64_input,
12947
    detail::output_span_of_byte_like auto &&binary_output,
12948
    base64_options options = base64_default,
12949
    last_chunk_handling_options last_chunk_options = loose,
12950
    bool decode_up_to_bad_char = false) noexcept {
12951
  size_t outlen = binary_output.size();
12952
  auto ret = atomic_base64_to_binary_safe(
12953
      base64_input.data(), base64_input.size(),
12954
      reinterpret_cast<char *>(binary_output.data()), outlen, options,
12955
      last_chunk_options, decode_up_to_bad_char);
12956
  return {ret, outlen};
12957
}
12958
    #endif // SIMDUTF_SPAN
12959
  #endif   // SIMDUTF_ATOMIC_REF
12960
12961
#endif // SIMDUTF_FEATURE_BASE64
12962
12963
/**
12964
 * An implementation of simdutf for a particular CPU architecture.
12965
 *
12966
 * Also used to maintain the currently active implementation. The active
12967
 * implementation is automatically initialized on first use to the most advanced
12968
 * implementation supported by the host.
12969
 */
12970
class implementation {
12971
public:
12972
  /**
12973
   * The name of this implementation.
12974
   *
12975
   *     const implementation *impl = simdutf::active_implementation;
12976
   *     cout << "simdutf is optimized for " << impl->name() << "(" <<
12977
   * impl->description() << ")" << endl;
12978
   *
12979
   * @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
12980
   */
12981
0
  virtual std::string_view name() const noexcept { return _name; }
12982
12983
  /**
12984
   * The description of this implementation.
12985
   *
12986
   *     const implementation *impl = simdutf::active_implementation;
12987
   *     cout << "simdutf is optimized for " << impl->name() << "(" <<
12988
   * impl->description() << ")" << endl;
12989
   *
12990
   * @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
12991
   */
12992
0
  virtual std::string_view description() const noexcept { return _description; }
12993
12994
  /**
12995
   * The instruction sets this implementation is compiled against
12996
   * and the current CPU match. This function may poll the current CPU/system
12997
   * and should therefore not be called too often if performance is a concern.
12998
   *
12999
   *
13000
   * @return true if the implementation can be safely used on the current system
13001
   * (determined at runtime)
13002
   */
13003
  bool supported_by_runtime_system() const;
13004
13005
#if SIMDUTF_FEATURE_DETECT_ENCODING
13006
  /**
13007
   * This function will try to detect the encoding
13008
   * @param input the string to identify
13009
   * @param length the length of the string in bytes.
13010
   * @return the encoding type detected
13011
   */
13012
  virtual encoding_type autodetect_encoding(const char *input,
13013
                                            size_t length) const noexcept;
13014
13015
  /**
13016
   * This function will try to detect the possible encodings in one pass
13017
   * @param input the string to identify
13018
   * @param length the length of the string in bytes.
13019
   * @return the encoding type detected
13020
   */
13021
  virtual int detect_encodings(const char *input,
13022
                               size_t length) const noexcept = 0;
13023
#endif // SIMDUTF_FEATURE_DETECT_ENCODING
13024
13025
  /**
13026
   * @private For internal implementation use
13027
   *
13028
   * The instruction sets this implementation is compiled against.
13029
   *
13030
   * @return a mask of all required `internal::instruction_set::` values
13031
   */
13032
2
  virtual uint32_t required_instruction_sets() const {
13033
2
    return _required_instruction_sets;
13034
2
  }
13035
13036
#if SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
13037
  /**
13038
   * Validate the UTF-8 string.
13039
   *
13040
   * Overridden by each implementation.
13041
   *
13042
   * @param buf the UTF-8 string to validate.
13043
   * @param len the length of the string in bytes.
13044
   * @return true if and only if the string is valid UTF-8.
13045
   */
13046
  simdutf_warn_unused virtual bool validate_utf8(const char *buf,
13047
                                                 size_t len) const noexcept = 0;
13048
#endif // SIMDUTF_FEATURE_UTF8 || SIMDUTF_FEATURE_DETECT_ENCODING
13049
13050
#if SIMDUTF_FEATURE_UTF8
13051
  /**
13052
   * Validate the UTF-8 string and stop on errors.
13053
   *
13054
   * Overridden by each implementation.
13055
   *
13056
   * @param buf the UTF-8 string to validate.
13057
   * @param len the length of the string in bytes.
13058
   * @return a result pair struct (of type simdutf::result containing the two
13059
   * fields error and count) with an error code and either position of the error
13060
   * (in the input in code units) if any, or the number of code units validated
13061
   * if successful.
13062
   */
13063
  simdutf_warn_unused virtual result
13064
  validate_utf8_with_errors(const char *buf, size_t len) const noexcept = 0;
13065
  /**
13066
   * Finds the pointer to the first byte of invalid utf8 while counting
13067
   continuation bytes and four-byte sequences.
13068
13069
   * @param buf the UTF-8 string to validate.
13070
   * @param len the length of the string in bytes.
13071
   * @return a utf8_result struct. It contains the length of the valid utf8
13072
   segment, the error code and the count of continuation bytes and four-byte
13073
   sequences.
13074
   *
13075
   * Returns a utf8_result
13076
   */
13077
  virtual simdutf_warn_unused utf8_result
13078
  validate_utf8_with_counts(const char *buf, size_t len) const noexcept = 0;
13079
13080
#endif // SIMDUTF_FEATURE_UTF8
13081
13082
#if SIMDUTF_FEATURE_ASCII
13083
  /**
13084
   * Validate the ASCII string.
13085
   *
13086
   * Overridden by each implementation.
13087
   *
13088
   * @param buf the ASCII string to validate.
13089
   * @param len the length of the string in bytes.
13090
   * @return true if and only if the string is valid ASCII.
13091
   */
13092
  simdutf_warn_unused virtual bool
13093
  validate_ascii(const char *buf, size_t len) const noexcept = 0;
13094
13095
  /**
13096
   * Validate the ASCII string and stop on error.
13097
   *
13098
   * Overridden by each implementation.
13099
   *
13100
   * @param buf the ASCII string to validate.
13101
   * @param len the length of the string in bytes.
13102
   * @return a result pair struct (of type simdutf::result containing the two
13103
   * fields error and count) with an error code and either position of the error
13104
   * (in the input in code units) if any, or the number of code units validated
13105
   * if successful.
13106
   */
13107
  simdutf_warn_unused virtual result
13108
  validate_ascii_with_errors(const char *buf, size_t len) const noexcept = 0;
13109
13110
#endif // SIMDUTF_FEATURE_ASCII
13111
13112
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
13113
  /**
13114
   * Validate the ASCII string as a UTF-16BE sequence.
13115
   * An UTF-16 sequence is considered an ASCII sequence
13116
   * if it could be converted to an ASCII string losslessly.
13117
   *
13118
   * Overridden by each implementation.
13119
   *
13120
   * @param buf the UTF-16BE string to validate.
13121
   * @param len the length of the string in bytes.
13122
   * @return true if and only if the string is valid ASCII.
13123
   */
13124
  simdutf_warn_unused virtual bool
13125
  validate_utf16be_as_ascii(const char16_t *buf, size_t len) const noexcept = 0;
13126
13127
  /**
13128
   * Validate the ASCII string as a UTF-16LE sequence.
13129
   * An UTF-16 sequence is considered an ASCII sequence
13130
   * if it could be converted to an ASCII string losslessly.
13131
   *
13132
   * Overridden by each implementation.
13133
   *
13134
   * @param buf the UTF-16LE string to validate.
13135
   * @param len the length of the string in bytes.
13136
   * @return true if and only if the string is valid ASCII.
13137
   */
13138
  simdutf_warn_unused virtual bool
13139
  validate_utf16le_as_ascii(const char16_t *buf, size_t len) const noexcept = 0;
13140
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_ASCII
13141
13142
#if SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
13143
  /**
13144
   * Validate the UTF-16LE string.This function may be best when you expect
13145
   * the input to be almost always valid. Otherwise, consider using
13146
   * validate_utf16le_with_errors.
13147
   *
13148
   * Overridden by each implementation.
13149
   *
13150
   * This function is not BOM-aware.
13151
   *
13152
   * @param buf the UTF-16LE string to validate.
13153
   * @param len the length of the string in number of 2-byte code units
13154
   * (char16_t).
13155
   * @return true if and only if the string is valid UTF-16LE.
13156
   */
13157
  simdutf_warn_unused virtual bool
13158
  validate_utf16le(const char16_t *buf, size_t len) const noexcept = 0;
13159
#endif // SIMDUTF_FEATURE_UTF16 || SIMDUTF_FEATURE_DETECT_ENCODING
13160
13161
#if SIMDUTF_FEATURE_UTF16
13162
  /**
13163
   * Validate the UTF-16BE string. This function may be best when you expect
13164
   * the input to be almost always valid. Otherwise, consider using
13165
   * validate_utf16be_with_errors.
13166
   *
13167
   * Overridden by each implementation.
13168
   *
13169
   * This function is not BOM-aware.
13170
   *
13171
   * @param buf the UTF-16BE string to validate.
13172
   * @param len the length of the string in number of 2-byte code units
13173
   * (char16_t).
13174
   * @return true if and only if the string is valid UTF-16BE.
13175
   */
13176
  simdutf_warn_unused virtual bool
13177
  validate_utf16be(const char16_t *buf, size_t len) const noexcept = 0;
13178
13179
  /**
13180
   * Validate the UTF-16LE string and stop on error.  It might be faster than
13181
   * validate_utf16le when an error is expected to occur early.
13182
   *
13183
   * Overridden by each implementation.
13184
   *
13185
   * This function is not BOM-aware.
13186
   *
13187
   * @param buf the UTF-16LE string to validate.
13188
   * @param len the length of the string in number of 2-byte code units
13189
   * (char16_t).
13190
   * @return a result pair struct (of type simdutf::result containing the two
13191
   * fields error and count) with an error code and either position of the error
13192
   * (in the input in code units) if any, or the number of code units validated
13193
   * if successful.
13194
   */
13195
  simdutf_warn_unused virtual result
13196
  validate_utf16le_with_errors(const char16_t *buf,
13197
                               size_t len) const noexcept = 0;
13198
13199
  /**
13200
   * Validate the UTF-16BE string and stop on error. It might be faster than
13201
   * validate_utf16be when an error is expected to occur early.
13202
   *
13203
   * Overridden by each implementation.
13204
   *
13205
   * This function is not BOM-aware.
13206
   *
13207
   * @param buf the UTF-16BE string to validate.
13208
   * @param len the length of the string in number of 2-byte code units
13209
   * (char16_t).
13210
   * @return a result pair struct (of type simdutf::result containing the two
13211
   * fields error and count) with an error code and either position of the error
13212
   * (in the input in code units) if any, or the number of code units validated
13213
   * if successful.
13214
   */
13215
  simdutf_warn_unused virtual result
13216
  validate_utf16be_with_errors(const char16_t *buf,
13217
                               size_t len) const noexcept = 0;
13218
  /**
13219
   * Copies the UTF-16LE string while replacing mismatched surrogates with the
13220
   * Unicode replacement character U+FFFD. We allow the input and output to be
13221
   * the same buffer so that the correction is done in-place.
13222
   *
13223
   * Overridden by each implementation.
13224
   *
13225
   * @param input the UTF-16LE string to correct.
13226
   * @param len the length of the string in number of 2-byte code units
13227
   * (char16_t).
13228
   * @param output the output buffer.
13229
   */
13230
  virtual void to_well_formed_utf16le(const char16_t *input, size_t len,
13231
                                      char16_t *output) const noexcept = 0;
13232
  /**
13233
   * Copies the UTF-16BE string while replacing mismatched surrogates with the
13234
   * Unicode replacement character U+FFFD. We allow the input and output to be
13235
   * the same buffer so that the correction is done in-place.
13236
   *
13237
   * Overridden by each implementation.
13238
   *
13239
   * @param input the UTF-16BE string to correct.
13240
   * @param len the length of the string in number of 2-byte code units
13241
   * (char16_t).
13242
   * @param output the output buffer.
13243
   */
13244
  virtual void to_well_formed_utf16be(const char16_t *input, size_t len,
13245
                                      char16_t *output) const noexcept = 0;
13246
#endif // SIMDUTF_FEATURE_UTF16
13247
13248
#if SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
13249
  /**
13250
   * Validate the UTF-32 string.
13251
   *
13252
   * Overridden by each implementation.
13253
   *
13254
   * This function is not BOM-aware.
13255
   *
13256
   * @param buf the UTF-32 string to validate.
13257
   * @param len the length of the string in number of 4-byte code units
13258
   * (char32_t).
13259
   * @return true if and only if the string is valid UTF-32.
13260
   */
13261
  simdutf_warn_unused virtual bool
13262
  validate_utf32(const char32_t *buf, size_t len) const noexcept = 0;
13263
#endif // SIMDUTF_FEATURE_UTF32 || SIMDUTF_FEATURE_DETECT_ENCODING
13264
13265
#if SIMDUTF_FEATURE_UTF32
13266
  /**
13267
   * Validate the UTF-32 string and stop on error.
13268
   *
13269
   * Overridden by each implementation.
13270
   *
13271
   * This function is not BOM-aware.
13272
   *
13273
   * @param buf the UTF-32 string to validate.
13274
   * @param len the length of the string in number of 4-byte code units
13275
   * (char32_t).
13276
   * @return a result pair struct (of type simdutf::result containing the two
13277
   * fields error and count) with an error code and either position of the error
13278
   * (in the input in code units) if any, or the number of code units validated
13279
   * if successful.
13280
   */
13281
  simdutf_warn_unused virtual result
13282
  validate_utf32_with_errors(const char32_t *buf,
13283
                             size_t len) const noexcept = 0;
13284
#endif // SIMDUTF_FEATURE_UTF32
13285
13286
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
13287
  /**
13288
   * Convert Latin1 string into UTF-8 string.
13289
   *
13290
   * This function is suitable to work with inputs from untrusted sources.
13291
   *
13292
   * @param input         the Latin1 string to convert
13293
   * @param length        the length of the string in bytes
13294
   * @param utf8_output  the pointer to buffer that can hold conversion result
13295
   * @return the number of written char; 0 if conversion is not possible
13296
   */
13297
  simdutf_warn_unused virtual size_t
13298
  convert_latin1_to_utf8(const char *input, size_t length,
13299
                         char *utf8_output) const noexcept = 0;
13300
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
13301
13302
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
13303
  /**
13304
   * Convert possibly Latin1 string into UTF-16LE string.
13305
   *
13306
   * This function is suitable to work with inputs from untrusted sources.
13307
   *
13308
   * @param input         the Latin1  string to convert
13309
   * @param length        the length of the string in bytes
13310
   * @param utf16_output  the pointer to buffer that can hold conversion result
13311
   * @return the number of written char16_t; 0 if conversion is not possible
13312
   */
13313
  simdutf_warn_unused virtual size_t
13314
  convert_latin1_to_utf16le(const char *input, size_t length,
13315
                            char16_t *utf16_output) const noexcept = 0;
13316
13317
  /**
13318
   * Convert Latin1 string into UTF-16BE string.
13319
   *
13320
   * This function is suitable to work with inputs from untrusted sources.
13321
   *
13322
   * @param input         the Latin1 string to convert
13323
   * @param length        the length of the string in bytes
13324
   * @param utf16_output  the pointer to buffer that can hold conversion result
13325
   * @return the number of written char16_t; 0 if conversion is not possible
13326
   */
13327
  simdutf_warn_unused virtual size_t
13328
  convert_latin1_to_utf16be(const char *input, size_t length,
13329
                            char16_t *utf16_output) const noexcept = 0;
13330
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
13331
13332
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
13333
  /**
13334
   * Convert Latin1 string into UTF-32 string.
13335
   *
13336
   * This function is suitable to work with inputs from untrusted sources.
13337
   *
13338
   * @param input         the Latin1 string to convert
13339
   * @param length        the length of the string in bytes
13340
   * @param utf32_buffer  the pointer to buffer that can hold conversion result
13341
   * @return the number of written char32_t; 0 if conversion is not possible
13342
   */
13343
  simdutf_warn_unused virtual size_t
13344
  convert_latin1_to_utf32(const char *input, size_t length,
13345
                          char32_t *utf32_buffer) const noexcept = 0;
13346
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
13347
13348
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
13349
  /**
13350
   * Convert possibly broken UTF-8 string into latin1 string.
13351
   *
13352
   * During the conversion also validation of the input string is done.
13353
   * This function is suitable to work with inputs from untrusted sources.
13354
   *
13355
   * @param input         the UTF-8 string to convert
13356
   * @param length        the length of the string in bytes
13357
   * @param latin1_output  the pointer to buffer that can hold conversion result
13358
   * @return the number of written char; 0 if the input was not valid UTF-8
13359
   * string or if it cannot be represented as Latin1
13360
   */
13361
  simdutf_warn_unused virtual size_t
13362
  convert_utf8_to_latin1(const char *input, size_t length,
13363
                         char *latin1_output) const noexcept = 0;
13364
13365
  /**
13366
   * Convert possibly broken UTF-8 string into latin1 string with errors.
13367
   * If the string cannot be represented as Latin1, an error
13368
   * code is returned.
13369
   *
13370
   * During the conversion also validation of the input string is done.
13371
   * This function is suitable to work with inputs from untrusted sources.
13372
   *
13373
   * @param input         the UTF-8 string to convert
13374
   * @param length        the length of the string in bytes
13375
   * @param latin1_output  the pointer to buffer that can hold conversion result
13376
   * @return a result pair struct (of type simdutf::result containing the two
13377
   * fields error and count) with an error code and either position of the error
13378
   * (in the input in code units) if any, or the number of code units validated
13379
   * if successful.
13380
   */
13381
  simdutf_warn_unused virtual result
13382
  convert_utf8_to_latin1_with_errors(const char *input, size_t length,
13383
                                     char *latin1_output) const noexcept = 0;
13384
13385
  /**
13386
   * Convert valid UTF-8 string into latin1 string.
13387
   *
13388
   * This function assumes that the input string is valid UTF-8 and that it can
13389
   * be represented as Latin1. If you violate this assumption, the result is
13390
   * implementation defined and may include system-dependent behavior such as
13391
   * crashes.
13392
   *
13393
   * This function is for expert users only and not part of our public API. Use
13394
   * convert_utf8_to_latin1 instead.
13395
   *
13396
   * This function is not BOM-aware.
13397
   *
13398
   * @param input         the UTF-8 string to convert
13399
   * @param length        the length of the string in bytes
13400
   * @param latin1_output  the pointer to buffer that can hold conversion result
13401
   * @return the number of written char; 0 if the input was not valid UTF-8
13402
   * string
13403
   */
13404
  simdutf_warn_unused virtual size_t
13405
  convert_valid_utf8_to_latin1(const char *input, size_t length,
13406
                               char *latin1_output) const noexcept = 0;
13407
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
13408
13409
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13410
  /**
13411
   * Convert possibly broken UTF-8 string into UTF-16LE string.
13412
   *
13413
   * During the conversion also validation of the input string is done.
13414
   * This function is suitable to work with inputs from untrusted sources.
13415
   *
13416
   * @param input         the UTF-8 string to convert
13417
   * @param length        the length of the string in bytes
13418
   * @param utf16_output  the pointer to buffer that can hold conversion result
13419
   * @return the number of written char16_t; 0 if the input was not valid UTF-8
13420
   * string
13421
   */
13422
  simdutf_warn_unused virtual size_t
13423
  convert_utf8_to_utf16le(const char *input, size_t length,
13424
                          char16_t *utf16_output) const noexcept = 0;
13425
13426
  /**
13427
   * Convert possibly broken UTF-8 string into UTF-16BE string.
13428
   *
13429
   * During the conversion also validation of the input string is done.
13430
   * This function is suitable to work with inputs from untrusted sources.
13431
   *
13432
   * @param input         the UTF-8 string to convert
13433
   * @param length        the length of the string in bytes
13434
   * @param utf16_output  the pointer to buffer that can hold conversion result
13435
   * @return the number of written char16_t; 0 if the input was not valid UTF-8
13436
   * string
13437
   */
13438
  simdutf_warn_unused virtual size_t
13439
  convert_utf8_to_utf16be(const char *input, size_t length,
13440
                          char16_t *utf16_output) const noexcept = 0;
13441
13442
  /**
13443
   * Convert possibly broken UTF-8 string into UTF-16LE string and stop on
13444
   * error.
13445
   *
13446
   * During the conversion also validation of the input string is done.
13447
   * This function is suitable to work with inputs from untrusted sources.
13448
   *
13449
   * @param input         the UTF-8 string to convert
13450
   * @param length        the length of the string in bytes
13451
   * @param utf16_output  the pointer to buffer that can hold conversion result
13452
   * @return a result pair struct (of type simdutf::result containing the two
13453
   * fields error and count) with an error code and either position of the error
13454
   * (in the input in code units) if any, or the number of code units validated
13455
   * if successful.
13456
   */
13457
  simdutf_warn_unused virtual result convert_utf8_to_utf16le_with_errors(
13458
      const char *input, size_t length,
13459
      char16_t *utf16_output) const noexcept = 0;
13460
13461
  /**
13462
   * Convert possibly broken UTF-8 string into UTF-16BE string and stop on
13463
   * error.
13464
   *
13465
   * During the conversion also validation of the input string is done.
13466
   * This function is suitable to work with inputs from untrusted sources.
13467
   *
13468
   * @param input         the UTF-8 string to convert
13469
   * @param length        the length of the string in bytes
13470
   * @param utf16_output  the pointer to buffer that can hold conversion result
13471
   * @return a result pair struct (of type simdutf::result containing the two
13472
   * fields error and count) with an error code and either position of the error
13473
   * (in the input in code units) if any, or the number of code units validated
13474
   * if successful.
13475
   */
13476
  simdutf_warn_unused virtual result convert_utf8_to_utf16be_with_errors(
13477
      const char *input, size_t length,
13478
      char16_t *utf16_output) const noexcept = 0;
13479
  /**
13480
   * Compute the number of bytes that this UTF-16LE string would require in
13481
   * UTF-8 format even when the UTF-16LE content contains mismatched
13482
   * surrogates that have to be replaced by the replacement character (0xFFFD).
13483
   *
13484
   * @param input         the UTF-16LE string to convert
13485
   * @param length        the length of the string in 2-byte code units
13486
   * (char16_t)
13487
   * @return a result pair struct (of type simdutf::result containing the two
13488
   * fields error and count) where the count is the number of bytes required to
13489
   * encode the UTF-16LE string as UTF-8, and the error code is either SUCCESS
13490
   * or SURROGATE. The count is correct regardless of the error field.
13491
   * When SURROGATE is returned, it does not indicate an error in the case of
13492
   * this function: it indicates that at least one surrogate has been
13493
   * encountered: the surrogates may be matched or not (thus this function does
13494
   * not validate). If the returned error code is SUCCESS, then the input
13495
   * contains no surrogate, is in the Basic Multilingual Plane, and is
13496
   * necessarily valid.
13497
   */
13498
  virtual simdutf_warn_unused result utf8_length_from_utf16le_with_replacement(
13499
      const char16_t *input, size_t length) const noexcept = 0;
13500
13501
  /**
13502
   * Compute the number of bytes that this UTF-16BE string would require in
13503
   * UTF-8 format even when the UTF-16BE content contains mismatched
13504
   * surrogates that have to be replaced by the replacement character (0xFFFD).
13505
   *
13506
   * @param input         the UTF-16BE string to convert
13507
   * @param length        the length of the string in 2-byte code units
13508
   * (char16_t)
13509
   * @return a result pair struct (of type simdutf::result containing the two
13510
   * fields error and count) where the count is the number of bytes required to
13511
   * encode the UTF-16BE string as UTF-8, and the error code is either SUCCESS
13512
   * or SURROGATE. The count is correct regardless of the error field.
13513
   * When SURROGATE is returned, it does not indicate an error in the case of
13514
   * this function: it indicates that at least one surrogate has been
13515
   * encountered: the surrogates may be matched or not (thus this function does
13516
   * not validate). If the returned error code is SUCCESS, then the input
13517
   * contains no surrogate, is in the Basic Multilingual Plane, and is
13518
   * necessarily valid.
13519
   */
13520
  virtual simdutf_warn_unused result utf8_length_from_utf16be_with_replacement(
13521
      const char16_t *input, size_t length) const noexcept = 0;
13522
13523
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13524
13525
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13526
  /**
13527
   * Convert possibly broken UTF-8 string into UTF-32 string.
13528
   *
13529
   * During the conversion also validation of the input string is done.
13530
   * This function is suitable to work with inputs from untrusted sources.
13531
   *
13532
   * @param input         the UTF-8 string to convert
13533
   * @param length        the length of the string in bytes
13534
   * @param utf32_output  the pointer to buffer that can hold conversion result
13535
   * @return the number of written char16_t; 0 if the input was not valid UTF-8
13536
   * string
13537
   */
13538
  simdutf_warn_unused virtual size_t
13539
  convert_utf8_to_utf32(const char *input, size_t length,
13540
                        char32_t *utf32_output) const noexcept = 0;
13541
13542
  /**
13543
   * Convert possibly broken UTF-8 string into UTF-32 string and stop on error.
13544
   *
13545
   * During the conversion also validation of the input string is done.
13546
   * This function is suitable to work with inputs from untrusted sources.
13547
   *
13548
   * @param input         the UTF-8 string to convert
13549
   * @param length        the length of the string in bytes
13550
   * @param utf32_output  the pointer to buffer that can hold conversion result
13551
   * @return a result pair struct (of type simdutf::result containing the two
13552
   * fields error and count) with an error code and either position of the error
13553
   * (in the input in code units) if any, or the number of char32_t written if
13554
   * successful.
13555
   */
13556
  simdutf_warn_unused virtual result
13557
  convert_utf8_to_utf32_with_errors(const char *input, size_t length,
13558
                                    char32_t *utf32_output) const noexcept = 0;
13559
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13560
13561
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13562
  /**
13563
   * Convert valid UTF-8 string into UTF-16LE string.
13564
   *
13565
   * This function assumes that the input string is valid UTF-8.
13566
   *
13567
   * @param input         the UTF-8 string to convert
13568
   * @param length        the length of the string in bytes
13569
   * @param utf16_buffer  the pointer to buffer that can hold conversion result
13570
   * @return the number of written char16_t
13571
   */
13572
  simdutf_warn_unused virtual size_t
13573
  convert_valid_utf8_to_utf16le(const char *input, size_t length,
13574
                                char16_t *utf16_buffer) const noexcept = 0;
13575
13576
  /**
13577
   * Convert valid UTF-8 string into UTF-16BE string.
13578
   *
13579
   * This function assumes that the input string is valid UTF-8.
13580
   *
13581
   * @param input         the UTF-8 string to convert
13582
   * @param length        the length of the string in bytes
13583
   * @param utf16_buffer  the pointer to buffer that can hold conversion result
13584
   * @return the number of written char16_t
13585
   */
13586
  simdutf_warn_unused virtual size_t
13587
  convert_valid_utf8_to_utf16be(const char *input, size_t length,
13588
                                char16_t *utf16_buffer) const noexcept = 0;
13589
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13590
13591
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13592
  /**
13593
   * Convert valid UTF-8 string into UTF-32 string.
13594
   *
13595
   * This function assumes that the input string is valid UTF-8.
13596
   *
13597
   * @param input         the UTF-8 string to convert
13598
   * @param length        the length of the string in bytes
13599
   * @param utf32_buffer  the pointer to buffer that can hold conversion result
13600
   * @return the number of written char32_t
13601
   */
13602
  simdutf_warn_unused virtual size_t
13603
  convert_valid_utf8_to_utf32(const char *input, size_t length,
13604
                              char32_t *utf32_buffer) const noexcept = 0;
13605
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13606
13607
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13608
  /**
13609
   * Compute the number of 2-byte code units that this UTF-8 string would
13610
   * require in UTF-16LE format.
13611
   *
13612
   * This function does not validate the input. It is acceptable to pass invalid
13613
   * UTF-8 strings but in such cases the result is implementation defined.
13614
   *
13615
   * @param input         the UTF-8 string to process
13616
   * @param length        the length of the string in bytes
13617
   * @return the number of char16_t code units required to encode the UTF-8
13618
   * string as UTF-16LE
13619
   */
13620
  simdutf_warn_unused virtual size_t
13621
  utf16_length_from_utf8(const char *input, size_t length) const noexcept = 0;
13622
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13623
13624
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13625
  /**
13626
   * Compute the number of 4-byte code units that this UTF-8 string would
13627
   * require in UTF-32 format.
13628
   *
13629
   * This function is equivalent to count_utf8. It is acceptable to pass invalid
13630
   * UTF-8 strings but in such cases the result is implementation defined.
13631
   *
13632
   * This function does not validate the input.
13633
   *
13634
   * @param input         the UTF-8 string to process
13635
   * @param length        the length of the string in bytes
13636
   * @return the number of char32_t code units required to encode the UTF-8
13637
   * string as UTF-32
13638
   */
13639
  simdutf_warn_unused virtual size_t
13640
  utf32_length_from_utf8(const char *input, size_t length) const noexcept = 0;
13641
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
13642
13643
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
13644
  /**
13645
   * Convert possibly broken UTF-16LE string into Latin1 string.
13646
   *
13647
   * During the conversion also validation of the input string is done.
13648
   * This function is suitable to work with inputs from untrusted sources.
13649
   *
13650
   * This function is not BOM-aware.
13651
   *
13652
   * @param input         the UTF-16LE string to convert
13653
   * @param length        the length of the string in 2-byte code units
13654
   * (char16_t)
13655
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13656
   * result
13657
   * @return number of written code units; 0 if input is not a valid UTF-16LE
13658
   * string or if it cannot be represented as Latin1
13659
   */
13660
  simdutf_warn_unused virtual size_t
13661
  convert_utf16le_to_latin1(const char16_t *input, size_t length,
13662
                            char *latin1_buffer) const noexcept = 0;
13663
13664
  /**
13665
   * Convert possibly broken UTF-16BE string into Latin1 string.
13666
   *
13667
   * During the conversion also validation of the input string is done.
13668
   * This function is suitable to work with inputs from untrusted sources.
13669
   *
13670
   * This function is not BOM-aware.
13671
   *
13672
   * @param input         the UTF-16BE string to convert
13673
   * @param length        the length of the string in 2-byte code units
13674
   * (char16_t)
13675
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13676
   * result
13677
   * @return number of written code units; 0 if input is not a valid UTF-16BE
13678
   * string or if it cannot be represented as Latin1
13679
   */
13680
  simdutf_warn_unused virtual size_t
13681
  convert_utf16be_to_latin1(const char16_t *input, size_t length,
13682
                            char *latin1_buffer) const noexcept = 0;
13683
13684
  /**
13685
   * Convert possibly broken UTF-16LE string into Latin1 string.
13686
   * If the string cannot be represented as Latin1, an error
13687
   * is returned.
13688
   *
13689
   * During the conversion also validation of the input string is done.
13690
   * This function is suitable to work with inputs from untrusted sources.
13691
   * This function is not BOM-aware.
13692
   *
13693
   * @param input         the UTF-16LE string to convert
13694
   * @param length        the length of the string in 2-byte code units
13695
   * (char16_t)
13696
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13697
   * result
13698
   * @return a result pair struct (of type simdutf::result containing the two
13699
   * fields error and count) with an error code and either position of the error
13700
   * (in the input in code units) if any, or the number of char written if
13701
   * successful.
13702
   */
13703
  simdutf_warn_unused virtual result
13704
  convert_utf16le_to_latin1_with_errors(const char16_t *input, size_t length,
13705
                                        char *latin1_buffer) const noexcept = 0;
13706
13707
  /**
13708
   * Convert possibly broken UTF-16BE string into Latin1 string.
13709
   * If the string cannot be represented as Latin1, an error
13710
   * is returned.
13711
   *
13712
   * During the conversion also validation of the input string is done.
13713
   * This function is suitable to work with inputs from untrusted sources.
13714
   * This function is not BOM-aware.
13715
   *
13716
   * @param input         the UTF-16BE string to convert
13717
   * @param length        the length of the string in 2-byte code units
13718
   * (char16_t)
13719
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13720
   * result
13721
   * @return a result pair struct (of type simdutf::result containing the two
13722
   * fields error and count) with an error code and either position of the error
13723
   * (in the input in code units) if any, or the number of char written if
13724
   * successful.
13725
   */
13726
  simdutf_warn_unused virtual result
13727
  convert_utf16be_to_latin1_with_errors(const char16_t *input, size_t length,
13728
                                        char *latin1_buffer) const noexcept = 0;
13729
13730
  /**
13731
   * Convert valid UTF-16LE string into Latin1 string.
13732
   *
13733
   * This function assumes that the input string is valid UTF-L16LE and that it
13734
   * can be represented as Latin1. If you violate this assumption, the result is
13735
   * implementation defined and may include system-dependent behavior such as
13736
   * crashes.
13737
   *
13738
   * This function is for expert users only and not part of our public API. Use
13739
   * convert_utf16le_to_latin1 instead.
13740
   *
13741
   * This function is not BOM-aware.
13742
   *
13743
   * @param input         the UTF-16LE string to convert
13744
   * @param length        the length of the string in 2-byte code units
13745
   * (char16_t)
13746
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13747
   * result
13748
   * @return number of written code units; 0 if conversion is not possible
13749
   */
13750
  simdutf_warn_unused virtual size_t
13751
  convert_valid_utf16le_to_latin1(const char16_t *input, size_t length,
13752
                                  char *latin1_buffer) const noexcept = 0;
13753
13754
  /**
13755
   * Convert valid UTF-16BE string into Latin1 string.
13756
   *
13757
   * This function assumes that the input string is valid UTF16-BE and that it
13758
   * can be represented as Latin1. If you violate this assumption, the result is
13759
   * implementation defined and may include system-dependent behavior such as
13760
   * crashes.
13761
   *
13762
   * This function is for expert users only and not part of our public API. Use
13763
   * convert_utf16be_to_latin1 instead.
13764
   *
13765
   * This function is not BOM-aware.
13766
   *
13767
   * @param input         the UTF-16BE string to convert
13768
   * @param length        the length of the string in 2-byte code units
13769
   * (char16_t)
13770
   * @param latin1_buffer   the pointer to buffer that can hold conversion
13771
   * result
13772
   * @return number of written code units; 0 if conversion is not possible
13773
   */
13774
  simdutf_warn_unused virtual size_t
13775
  convert_valid_utf16be_to_latin1(const char16_t *input, size_t length,
13776
                                  char *latin1_buffer) const noexcept = 0;
13777
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
13778
13779
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13780
  /**
13781
   * Convert possibly broken UTF-16LE string into UTF-8 string.
13782
   *
13783
   * During the conversion also validation of the input string is done.
13784
   * This function is suitable to work with inputs from untrusted sources.
13785
   *
13786
   * This function is not BOM-aware.
13787
   *
13788
   * @param input         the UTF-16LE string to convert
13789
   * @param length        the length of the string in 2-byte code units
13790
   * (char16_t)
13791
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13792
   * @return number of written code units; 0 if input is not a valid UTF-16LE
13793
   * string
13794
   */
13795
  simdutf_warn_unused virtual size_t
13796
  convert_utf16le_to_utf8(const char16_t *input, size_t length,
13797
                          char *utf8_buffer) const noexcept = 0;
13798
13799
  /**
13800
   * Convert possibly broken UTF-16BE string into UTF-8 string.
13801
   *
13802
   * During the conversion also validation of the input string is done.
13803
   * This function is suitable to work with inputs from untrusted sources.
13804
   *
13805
   * This function is not BOM-aware.
13806
   *
13807
   * @param input         the UTF-16BE string to convert
13808
   * @param length        the length of the string in 2-byte code units
13809
   * (char16_t)
13810
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13811
   * @return number of written code units; 0 if input is not a valid UTF-16BE
13812
   * string
13813
   */
13814
  simdutf_warn_unused virtual size_t
13815
  convert_utf16be_to_utf8(const char16_t *input, size_t length,
13816
                          char *utf8_buffer) const noexcept = 0;
13817
13818
  /**
13819
   * Convert possibly broken UTF-16LE string into UTF-8 string and stop on
13820
   * error.
13821
   *
13822
   * During the conversion also validation of the input string is done.
13823
   * This function is suitable to work with inputs from untrusted sources.
13824
   *
13825
   * This function is not BOM-aware.
13826
   *
13827
   * @param input         the UTF-16LE string to convert
13828
   * @param length        the length of the string in 2-byte code units
13829
   * (char16_t)
13830
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13831
   * @return a result pair struct (of type simdutf::result containing the two
13832
   * fields error and count) with an error code and either position of the error
13833
   * (in the input in code units) if any, or the number of char written if
13834
   * successful.
13835
   */
13836
  simdutf_warn_unused virtual result
13837
  convert_utf16le_to_utf8_with_errors(const char16_t *input, size_t length,
13838
                                      char *utf8_buffer) const noexcept = 0;
13839
13840
  /**
13841
   * Convert possibly broken UTF-16BE string into UTF-8 string and stop on
13842
   * error.
13843
   *
13844
   * During the conversion also validation of the input string is done.
13845
   * This function is suitable to work with inputs from untrusted sources.
13846
   *
13847
   * This function is not BOM-aware.
13848
   *
13849
   * @param input         the UTF-16BE string to convert
13850
   * @param length        the length of the string in 2-byte code units
13851
   * (char16_t)
13852
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13853
   * @return a result pair struct (of type simdutf::result containing the two
13854
   * fields error and count) with an error code and either position of the error
13855
   * (in the input in code units) if any, or the number of char written if
13856
   * successful.
13857
   */
13858
  simdutf_warn_unused virtual result
13859
  convert_utf16be_to_utf8_with_errors(const char16_t *input, size_t length,
13860
                                      char *utf8_buffer) const noexcept = 0;
13861
13862
  /**
13863
   * Convert possibly broken UTF-16LE string into UTF-8 string, replacing
13864
   * unpaired surrogates with the Unicode replacement character U+FFFD.
13865
   *
13866
   * This function always succeeds: unpaired surrogates are replaced with
13867
   * U+FFFD (3 bytes in UTF-8: 0xEF 0xBF 0xBD).
13868
   *
13869
   * This function is not BOM-aware.
13870
   *
13871
   * @param input         the UTF-16LE string to convert
13872
   * @param length        the length of the string in 2-byte code units
13873
   * (char16_t)
13874
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13875
   * @return number of written code units
13876
   */
13877
  simdutf_warn_unused virtual size_t convert_utf16le_to_utf8_with_replacement(
13878
      const char16_t *input, size_t length,
13879
      char *utf8_buffer) const noexcept = 0;
13880
13881
  /**
13882
   * Convert possibly broken UTF-16BE string into UTF-8 string, replacing
13883
   * unpaired surrogates with the Unicode replacement character U+FFFD.
13884
   *
13885
   * This function always succeeds: unpaired surrogates are replaced with
13886
   * U+FFFD (3 bytes in UTF-8: 0xEF 0xBF 0xBD).
13887
   *
13888
   * This function is not BOM-aware.
13889
   *
13890
   * @param input         the UTF-16BE string to convert
13891
   * @param length        the length of the string in 2-byte code units
13892
   * (char16_t)
13893
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
13894
   * @return number of written code units
13895
   */
13896
  simdutf_warn_unused virtual size_t convert_utf16be_to_utf8_with_replacement(
13897
      const char16_t *input, size_t length,
13898
      char *utf8_buffer) const noexcept = 0;
13899
13900
  /**
13901
   * Convert valid UTF-16LE string into UTF-8 string.
13902
   *
13903
   * This function assumes that the input string is valid UTF-16LE.
13904
   *
13905
   * This function is not BOM-aware.
13906
   *
13907
   * @param input         the UTF-16LE string to convert
13908
   * @param length        the length of the string in 2-byte code units
13909
   * (char16_t)
13910
   * @param utf8_buffer   the pointer to a buffer that can hold the conversion
13911
   * result
13912
   * @return number of written code units; 0 if conversion is not possible
13913
   */
13914
  simdutf_warn_unused virtual size_t
13915
  convert_valid_utf16le_to_utf8(const char16_t *input, size_t length,
13916
                                char *utf8_buffer) const noexcept = 0;
13917
13918
  /**
13919
   * Convert valid UTF-16BE string into UTF-8 string.
13920
   *
13921
   * This function assumes that the input string is valid UTF-16BE.
13922
   *
13923
   * This function is not BOM-aware.
13924
   *
13925
   * @param input         the UTF-16BE string to convert
13926
   * @param length        the length of the string in 2-byte code units
13927
   * (char16_t)
13928
   * @param utf8_buffer   the pointer to a buffer that can hold the conversion
13929
   * result
13930
   * @return number of written code units; 0 if conversion is not possible
13931
   */
13932
  simdutf_warn_unused virtual size_t
13933
  convert_valid_utf16be_to_utf8(const char16_t *input, size_t length,
13934
                                char *utf8_buffer) const noexcept = 0;
13935
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
13936
13937
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
13938
  /**
13939
   * Convert possibly broken UTF-16LE string into UTF-32 string.
13940
   *
13941
   * During the conversion also validation of the input string is done.
13942
   * This function is suitable to work with inputs from untrusted sources.
13943
   *
13944
   * This function is not BOM-aware.
13945
   *
13946
   * @param input         the UTF-16LE string to convert
13947
   * @param length        the length of the string in 2-byte code units
13948
   * (char16_t)
13949
   * @param utf32_buffer   the pointer to buffer that can hold conversion result
13950
   * @return number of written code units; 0 if input is not a valid UTF-16LE
13951
   * string
13952
   */
13953
  simdutf_warn_unused virtual size_t
13954
  convert_utf16le_to_utf32(const char16_t *input, size_t length,
13955
                           char32_t *utf32_buffer) const noexcept = 0;
13956
13957
  /**
13958
   * Convert possibly broken UTF-16BE string into UTF-32 string.
13959
   *
13960
   * During the conversion also validation of the input string is done.
13961
   * This function is suitable to work with inputs from untrusted sources.
13962
   *
13963
   * This function is not BOM-aware.
13964
   *
13965
   * @param input         the UTF-16BE string to convert
13966
   * @param length        the length of the string in 2-byte code units
13967
   * (char16_t)
13968
   * @param utf32_buffer   the pointer to buffer that can hold conversion result
13969
   * @return number of written code units; 0 if input is not a valid UTF-16BE
13970
   * string
13971
   */
13972
  simdutf_warn_unused virtual size_t
13973
  convert_utf16be_to_utf32(const char16_t *input, size_t length,
13974
                           char32_t *utf32_buffer) const noexcept = 0;
13975
13976
  /**
13977
   * Convert possibly broken UTF-16LE string into UTF-32 string and stop on
13978
   * error.
13979
   *
13980
   * During the conversion also validation of the input string is done.
13981
   * This function is suitable to work with inputs from untrusted sources.
13982
   *
13983
   * This function is not BOM-aware.
13984
   *
13985
   * @param input         the UTF-16LE string to convert
13986
   * @param length        the length of the string in 2-byte code units
13987
   * (char16_t)
13988
   * @param utf32_buffer   the pointer to buffer that can hold conversion result
13989
   * @return a result pair struct (of type simdutf::result containing the two
13990
   * fields error and count) with an error code and either position of the error
13991
   * (in the input in code units) if any, or the number of char32_t written if
13992
   * successful.
13993
   */
13994
  simdutf_warn_unused virtual result convert_utf16le_to_utf32_with_errors(
13995
      const char16_t *input, size_t length,
13996
      char32_t *utf32_buffer) const noexcept = 0;
13997
13998
  /**
13999
   * Convert possibly broken UTF-16BE string into UTF-32 string and stop on
14000
   * error.
14001
   *
14002
   * During the conversion also validation of the input string is done.
14003
   * This function is suitable to work with inputs from untrusted sources.
14004
   *
14005
   * This function is not BOM-aware.
14006
   *
14007
   * @param input         the UTF-16BE string to convert
14008
   * @param length        the length of the string in 2-byte code units
14009
   * (char16_t)
14010
   * @param utf32_buffer   the pointer to buffer that can hold conversion result
14011
   * @return a result pair struct (of type simdutf::result containing the two
14012
   * fields error and count) with an error code and either position of the error
14013
   * (in the input in code units) if any, or the number of char32_t written if
14014
   * successful.
14015
   */
14016
  simdutf_warn_unused virtual result convert_utf16be_to_utf32_with_errors(
14017
      const char16_t *input, size_t length,
14018
      char32_t *utf32_buffer) const noexcept = 0;
14019
14020
  /**
14021
   * Convert valid UTF-16LE string into UTF-32 string.
14022
   *
14023
   * This function assumes that the input string is valid UTF-16LE.
14024
   *
14025
   * This function is not BOM-aware.
14026
   *
14027
   * @param input         the UTF-16LE string to convert
14028
   * @param length        the length of the string in 2-byte code units
14029
   * (char16_t)
14030
   * @param utf32_buffer   the pointer to a buffer that can hold the conversion
14031
   * result
14032
   * @return number of written code units; 0 if conversion is not possible
14033
   */
14034
  simdutf_warn_unused virtual size_t
14035
  convert_valid_utf16le_to_utf32(const char16_t *input, size_t length,
14036
                                 char32_t *utf32_buffer) const noexcept = 0;
14037
14038
  /**
14039
   * Convert valid UTF-16LE string into UTF-32BE string.
14040
   *
14041
   * This function assumes that the input string is valid UTF-16BE.
14042
   *
14043
   * This function is not BOM-aware.
14044
   *
14045
   * @param input         the UTF-16BE string to convert
14046
   * @param length        the length of the string in 2-byte code units
14047
   * (char16_t)
14048
   * @param utf32_buffer   the pointer to a buffer that can hold the conversion
14049
   * result
14050
   * @return number of written code units; 0 if conversion is not possible
14051
   */
14052
  simdutf_warn_unused virtual size_t
14053
  convert_valid_utf16be_to_utf32(const char16_t *input, size_t length,
14054
                                 char32_t *utf32_buffer) const noexcept = 0;
14055
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14056
14057
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
14058
  /**
14059
   * Compute the number of bytes that this UTF-16LE string would require in
14060
   * UTF-8 format.
14061
   *
14062
   * This function does not validate the input. It is acceptable to pass invalid
14063
   * UTF-16 strings but in such cases the result is implementation defined.
14064
   *
14065
   * This function is not BOM-aware.
14066
   *
14067
   * @param input         the UTF-16LE string to convert
14068
   * @param length        the length of the string in 2-byte code units
14069
   * (char16_t)
14070
   * @return the number of bytes required to encode the UTF-16LE string as UTF-8
14071
   */
14072
  simdutf_warn_unused virtual size_t
14073
  utf8_length_from_utf16le(const char16_t *input,
14074
                           size_t length) const noexcept = 0;
14075
14076
  /**
14077
   * Compute the number of bytes that this UTF-16BE string would require in
14078
   * UTF-8 format.
14079
   *
14080
   * This function does not validate the input. It is acceptable to pass invalid
14081
   * UTF-16 strings but in such cases the result is implementation defined.
14082
   *
14083
   * This function is not BOM-aware.
14084
   *
14085
   * @param input         the UTF-16BE string to convert
14086
   * @param length        the length of the string in 2-byte code units
14087
   * (char16_t)
14088
   * @return the number of bytes required to encode the UTF-16BE string as UTF-8
14089
   */
14090
  simdutf_warn_unused virtual size_t
14091
  utf8_length_from_utf16be(const char16_t *input,
14092
                           size_t length) const noexcept = 0;
14093
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF16
14094
14095
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14096
  /**
14097
   * Convert possibly broken UTF-32 string into Latin1 string.
14098
   *
14099
   * During the conversion also validation of the input string is done.
14100
   * This function is suitable to work with inputs from untrusted sources.
14101
   *
14102
   * This function is not BOM-aware.
14103
   *
14104
   * @param input         the UTF-32 string to convert
14105
   * @param length        the length of the string in 4-byte code units
14106
   * (char32_t)
14107
   * @param latin1_buffer   the pointer to buffer that can hold conversion
14108
   * result
14109
   * @return number of written code units; 0 if input is not a valid UTF-32
14110
   * string
14111
   */
14112
  simdutf_warn_unused virtual size_t
14113
  convert_utf32_to_latin1(const char32_t *input, size_t length,
14114
                          char *latin1_buffer) const noexcept = 0;
14115
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14116
14117
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14118
  /**
14119
   * Convert possibly broken UTF-32 string into Latin1 string and stop on error.
14120
   * If the string cannot be represented as Latin1, an error is returned.
14121
   *
14122
   * During the conversion also validation of the input string is done.
14123
   * This function is suitable to work with inputs from untrusted sources.
14124
   *
14125
   * This function is not BOM-aware.
14126
   *
14127
   * @param input         the UTF-32 string to convert
14128
   * @param length        the length of the string in 4-byte code units
14129
   * (char32_t)
14130
   * @param latin1_buffer   the pointer to buffer that can hold conversion
14131
   * result
14132
   * @return a result pair struct (of type simdutf::result containing the two
14133
   * fields error and count) with an error code and either position of the error
14134
   * (in the input in code units) if any, or the number of char written if
14135
   * successful.
14136
   */
14137
  simdutf_warn_unused virtual result
14138
  convert_utf32_to_latin1_with_errors(const char32_t *input, size_t length,
14139
                                      char *latin1_buffer) const noexcept = 0;
14140
14141
  /**
14142
   * Convert valid UTF-32 string into Latin1 string.
14143
   *
14144
   * This function assumes that the input string is valid UTF-32 and can be
14145
   * represented as Latin1. If you violate this assumption, the result is
14146
   * implementation defined and may include system-dependent behavior such as
14147
   * crashes.
14148
   *
14149
   * This function is for expert users only and not part of our public API. Use
14150
   * convert_utf32_to_latin1 instead.
14151
   *
14152
   * This function is not BOM-aware.
14153
   *
14154
   * @param input         the UTF-32 string to convert
14155
   * @param length        the length of the string in 4-byte code units
14156
   * (char32_t)
14157
   * @param latin1_buffer   the pointer to a buffer that can hold the conversion
14158
   * result
14159
   * @return number of written code units; 0 if conversion is not possible
14160
   */
14161
  simdutf_warn_unused virtual size_t
14162
  convert_valid_utf32_to_latin1(const char32_t *input, size_t length,
14163
                                char *latin1_buffer) const noexcept = 0;
14164
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14165
14166
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
14167
  /**
14168
   * Convert possibly broken UTF-32 string into UTF-8 string.
14169
   *
14170
   * During the conversion also validation of the input string is done.
14171
   * This function is suitable to work with inputs from untrusted sources.
14172
   *
14173
   * This function is not BOM-aware.
14174
   *
14175
   * @param input         the UTF-32 string to convert
14176
   * @param length        the length of the string in 4-byte code units
14177
   * (char32_t)
14178
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
14179
   * @return number of written code units; 0 if input is not a valid UTF-32
14180
   * string
14181
   */
14182
  simdutf_warn_unused virtual size_t
14183
  convert_utf32_to_utf8(const char32_t *input, size_t length,
14184
                        char *utf8_buffer) const noexcept = 0;
14185
14186
  /**
14187
   * Convert possibly broken UTF-32 string into UTF-8 string and stop on error.
14188
   *
14189
   * During the conversion also validation of the input string is done.
14190
   * This function is suitable to work with inputs from untrusted sources.
14191
   *
14192
   * This function is not BOM-aware.
14193
   *
14194
   * @param input         the UTF-32 string to convert
14195
   * @param length        the length of the string in 4-byte code units
14196
   * (char32_t)
14197
   * @param utf8_buffer   the pointer to buffer that can hold conversion result
14198
   * @return a result pair struct (of type simdutf::result containing the two
14199
   * fields error and count) with an error code and either position of the error
14200
   * (in the input in code units) if any, or the number of char written if
14201
   * successful.
14202
   */
14203
  simdutf_warn_unused virtual result
14204
  convert_utf32_to_utf8_with_errors(const char32_t *input, size_t length,
14205
                                    char *utf8_buffer) const noexcept = 0;
14206
14207
  /**
14208
   * Convert valid UTF-32 string into UTF-8 string.
14209
   *
14210
   * This function assumes that the input string is valid UTF-32.
14211
   *
14212
   * This function is not BOM-aware.
14213
   *
14214
   * @param input         the UTF-32 string to convert
14215
   * @param length        the length of the string in 4-byte code units
14216
   * (char32_t)
14217
   * @param utf8_buffer   the pointer to a buffer that can hold the conversion
14218
   * result
14219
   * @return number of written code units; 0 if conversion is not possible
14220
   */
14221
  simdutf_warn_unused virtual size_t
14222
  convert_valid_utf32_to_utf8(const char32_t *input, size_t length,
14223
                              char *utf8_buffer) const noexcept = 0;
14224
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
14225
14226
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
14227
  /**
14228
   * Return the number of bytes that this UTF-16 string would require in Latin1
14229
   * format.
14230
   *
14231
   *
14232
   * @param length        the length of the string in 2-byte code units
14233
   * (char16_t)
14234
   * @return the number of bytes required to encode the UTF-16 string as Latin1
14235
   */
14236
  simdutf_warn_unused virtual size_t
14237
  utf16_length_from_latin1(size_t length) const noexcept {
14238
    return length;
14239
  }
14240
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
14241
14242
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14243
  /**
14244
   * Convert possibly broken UTF-32 string into UTF-16LE string.
14245
   *
14246
   * During the conversion also validation of the input string is done.
14247
   * This function is suitable to work with inputs from untrusted sources.
14248
   *
14249
   * This function is not BOM-aware.
14250
   *
14251
   * @param input         the UTF-32 string to convert
14252
   * @param length        the length of the string in 4-byte code units
14253
   * (char32_t)
14254
   * @param utf16_buffer   the pointer to buffer that can hold conversion result
14255
   * @return number of written code units; 0 if input is not a valid UTF-32
14256
   * string
14257
   */
14258
  simdutf_warn_unused virtual size_t
14259
  convert_utf32_to_utf16le(const char32_t *input, size_t length,
14260
                           char16_t *utf16_buffer) const noexcept = 0;
14261
14262
  /**
14263
   * Convert possibly broken UTF-32 string into UTF-16BE string.
14264
   *
14265
   * During the conversion also validation of the input string is done.
14266
   * This function is suitable to work with inputs from untrusted sources.
14267
   *
14268
   * This function is not BOM-aware.
14269
   *
14270
   * @param input         the UTF-32 string to convert
14271
   * @param length        the length of the string in 4-byte code units
14272
   * (char32_t)
14273
   * @param utf16_buffer   the pointer to buffer that can hold conversion result
14274
   * @return number of written code units; 0 if input is not a valid UTF-32
14275
   * string
14276
   */
14277
  simdutf_warn_unused virtual size_t
14278
  convert_utf32_to_utf16be(const char32_t *input, size_t length,
14279
                           char16_t *utf16_buffer) const noexcept = 0;
14280
14281
  /**
14282
   * Convert possibly broken UTF-32 string into UTF-16LE string and stop on
14283
   * error.
14284
   *
14285
   * During the conversion also validation of the input string is done.
14286
   * This function is suitable to work with inputs from untrusted sources.
14287
   *
14288
   * This function is not BOM-aware.
14289
   *
14290
   * @param input         the UTF-32 string to convert
14291
   * @param length        the length of the string in 4-byte code units
14292
   * (char32_t)
14293
   * @param utf16_buffer   the pointer to buffer that can hold conversion result
14294
   * @return a result pair struct (of type simdutf::result containing the two
14295
   * fields error and count) with an error code and either position of the error
14296
   * (in the input in code units) if any, or the number of char16_t written if
14297
   * successful.
14298
   */
14299
  simdutf_warn_unused virtual result convert_utf32_to_utf16le_with_errors(
14300
      const char32_t *input, size_t length,
14301
      char16_t *utf16_buffer) const noexcept = 0;
14302
14303
  /**
14304
   * Convert possibly broken UTF-32 string into UTF-16BE string and stop on
14305
   * error.
14306
   *
14307
   * During the conversion also validation of the input string is done.
14308
   * This function is suitable to work with inputs from untrusted sources.
14309
   *
14310
   * This function is not BOM-aware.
14311
   *
14312
   * @param input         the UTF-32 string to convert
14313
   * @param length        the length of the string in 4-byte code units
14314
   * (char32_t)
14315
   * @param utf16_buffer   the pointer to buffer that can hold conversion result
14316
   * @return a result pair struct (of type simdutf::result containing the two
14317
   * fields error and count) with an error code and either position of the error
14318
   * (in the input in code units) if any, or the number of char16_t written if
14319
   * successful.
14320
   */
14321
  simdutf_warn_unused virtual result convert_utf32_to_utf16be_with_errors(
14322
      const char32_t *input, size_t length,
14323
      char16_t *utf16_buffer) const noexcept = 0;
14324
14325
  /**
14326
   * Convert valid UTF-32 string into UTF-16LE string.
14327
   *
14328
   * This function assumes that the input string is valid UTF-32.
14329
   *
14330
   * This function is not BOM-aware.
14331
   *
14332
   * @param input         the UTF-32 string to convert
14333
   * @param length        the length of the string in 4-byte code units
14334
   * (char32_t)
14335
   * @param utf16_buffer   the pointer to a buffer that can hold the conversion
14336
   * result
14337
   * @return number of written code units; 0 if conversion is not possible
14338
   */
14339
  simdutf_warn_unused virtual size_t
14340
  convert_valid_utf32_to_utf16le(const char32_t *input, size_t length,
14341
                                 char16_t *utf16_buffer) const noexcept = 0;
14342
14343
  /**
14344
   * Convert valid UTF-32 string into UTF-16BE string.
14345
   *
14346
   * This function assumes that the input string is valid UTF-32.
14347
   *
14348
   * This function is not BOM-aware.
14349
   *
14350
   * @param input         the UTF-32 string to convert
14351
   * @param length        the length of the string in 4-byte code units
14352
   * (char32_t)
14353
   * @param utf16_buffer   the pointer to a buffer that can hold the conversion
14354
   * result
14355
   * @return number of written code units; 0 if conversion is not possible
14356
   */
14357
  simdutf_warn_unused virtual size_t
14358
  convert_valid_utf32_to_utf16be(const char32_t *input, size_t length,
14359
                                 char16_t *utf16_buffer) const noexcept = 0;
14360
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14361
14362
#if SIMDUTF_FEATURE_UTF16
14363
  /**
14364
   * Change the endianness of the input. Can be used to go from UTF-16LE to
14365
   * UTF-16BE or from UTF-16BE to UTF-16LE.
14366
   *
14367
   * This function does not validate the input.
14368
   *
14369
   * This function is not BOM-aware.
14370
   *
14371
   * @param input         the UTF-16 string to process
14372
   * @param length        the length of the string in 2-byte code units
14373
   * (char16_t)
14374
   * @param output        the pointer to a buffer that can hold the conversion
14375
   * result
14376
   */
14377
  virtual void change_endianness_utf16(const char16_t *input, size_t length,
14378
                                       char16_t *output) const noexcept = 0;
14379
#endif // SIMDUTF_FEATURE_UTF16
14380
14381
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
14382
  /**
14383
   * Return the number of bytes that this Latin1 string would require in UTF-8
14384
   * format.
14385
   *
14386
   * @param input         the Latin1 string to convert
14387
   * @param length        the length of the string bytes
14388
   * @return the number of bytes required to encode the Latin1 string as UTF-8
14389
   */
14390
  simdutf_warn_unused virtual size_t
14391
  utf8_length_from_latin1(const char *input, size_t length) const noexcept = 0;
14392
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
14393
14394
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
14395
  /**
14396
   * Compute the number of bytes that this UTF-32 string would require in UTF-8
14397
   * format.
14398
   *
14399
   * This function does not validate the input. It is acceptable to pass invalid
14400
   * UTF-32 strings but in such cases the result is implementation defined.
14401
   *
14402
   * @param input         the UTF-32 string to convert
14403
   * @param length        the length of the string in 4-byte code units
14404
   * (char32_t)
14405
   * @return the number of bytes required to encode the UTF-32 string as UTF-8
14406
   */
14407
  simdutf_warn_unused virtual size_t
14408
  utf8_length_from_utf32(const char32_t *input,
14409
                         size_t length) const noexcept = 0;
14410
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_UTF32
14411
14412
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14413
  /**
14414
   * Compute the number of bytes that this UTF-32 string would require in Latin1
14415
   * format.
14416
   *
14417
   * This function does not validate the input. It is acceptable to pass invalid
14418
   * UTF-32 strings but in such cases the result is implementation defined.
14419
   *
14420
   * @param length        the length of the string in 4-byte code units
14421
   * (char32_t)
14422
   * @return the number of bytes required to encode the UTF-32 string as Latin1
14423
   */
14424
  simdutf_warn_unused virtual size_t
14425
  latin1_length_from_utf32(size_t length) const noexcept {
14426
    return length;
14427
  }
14428
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14429
14430
#if SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
14431
  /**
14432
   * Compute the number of bytes that this UTF-8 string would require in Latin1
14433
   * format.
14434
   *
14435
   * This function does not validate the input. It is acceptable to pass invalid
14436
   * UTF-8 strings but in such cases the result is implementation defined.
14437
   *
14438
   * @param input         the UTF-8 string to convert
14439
   * @param length        the length of the string in byte
14440
   * @return the number of bytes required to encode the UTF-8 string as Latin1
14441
   */
14442
  simdutf_warn_unused virtual size_t
14443
  latin1_length_from_utf8(const char *input, size_t length) const noexcept = 0;
14444
#endif // SIMDUTF_FEATURE_UTF8 && SIMDUTF_FEATURE_LATIN1
14445
14446
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
14447
  /**
14448
   * Compute the number of bytes that this UTF-16LE/BE string would require in
14449
   * Latin1 format.
14450
   *
14451
   * This function does not validate the input. It is acceptable to pass invalid
14452
   * UTF-16 strings but in such cases the result is implementation defined.
14453
   *
14454
   * This function is not BOM-aware.
14455
   *
14456
   * @param length        the length of the string in 2-byte code units
14457
   * (char16_t)
14458
   * @return the number of bytes required to encode the UTF-16LE string as
14459
   * Latin1
14460
   */
14461
  simdutf_warn_unused virtual size_t
14462
  latin1_length_from_utf16(size_t length) const noexcept {
14463
    return length;
14464
  }
14465
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_LATIN1
14466
14467
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14468
  /**
14469
   * Compute the number of two-byte code units that this UTF-32 string would
14470
   * require in UTF-16 format.
14471
   *
14472
   * This function does not validate the input. It is acceptable to pass invalid
14473
   * UTF-32 strings but in such cases the result is implementation defined.
14474
   *
14475
   * @param input         the UTF-32 string to convert
14476
   * @param length        the length of the string in 4-byte code units
14477
   * (char32_t)
14478
   * @return the number of bytes required to encode the UTF-32 string as UTF-16
14479
   */
14480
  simdutf_warn_unused virtual size_t
14481
  utf16_length_from_utf32(const char32_t *input,
14482
                          size_t length) const noexcept = 0;
14483
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14484
14485
#if SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14486
  /**
14487
   * Return the number of bytes that this UTF-32 string would require in Latin1
14488
   * format.
14489
   *
14490
   * @param length        the length of the string in 4-byte code units
14491
   * (char32_t)
14492
   * @return the number of bytes required to encode the UTF-32 string as Latin1
14493
   */
14494
  simdutf_warn_unused virtual size_t
14495
  utf32_length_from_latin1(size_t length) const noexcept {
14496
    return length;
14497
  }
14498
#endif // SIMDUTF_FEATURE_UTF32 && SIMDUTF_FEATURE_LATIN1
14499
14500
#if SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14501
  /**
14502
   * Compute the number of bytes that this UTF-16LE string would require in
14503
   * UTF-32 format.
14504
   *
14505
   * This function is equivalent to count_utf16le.
14506
   *
14507
   * This function does not validate the input. It is acceptable to pass invalid
14508
   * UTF-16 strings but in such cases the result is implementation defined.
14509
   *
14510
   * This function is not BOM-aware.
14511
   *
14512
   * @param input         the UTF-16LE string to convert
14513
   * @param length        the length of the string in 2-byte code units
14514
   * (char16_t)
14515
   * @return the number of bytes required to encode the UTF-16LE string as
14516
   * UTF-32
14517
   */
14518
  simdutf_warn_unused virtual size_t
14519
  utf32_length_from_utf16le(const char16_t *input,
14520
                            size_t length) const noexcept = 0;
14521
14522
  /**
14523
   * Compute the number of bytes that this UTF-16BE string would require in
14524
   * UTF-32 format.
14525
   *
14526
   * This function is equivalent to count_utf16be.
14527
   *
14528
   * This function does not validate the input. It is acceptable to pass invalid
14529
   * UTF-16 strings but in such cases the result is implementation defined.
14530
   *
14531
   * This function is not BOM-aware.
14532
   *
14533
   * @param input         the UTF-16BE string to convert
14534
   * @param length        the length of the string in 2-byte code units
14535
   * (char16_t)
14536
   * @return the number of bytes required to encode the UTF-16BE string as
14537
   * UTF-32
14538
   */
14539
  simdutf_warn_unused virtual size_t
14540
  utf32_length_from_utf16be(const char16_t *input,
14541
                            size_t length) const noexcept = 0;
14542
#endif // SIMDUTF_FEATURE_UTF16 && SIMDUTF_FEATURE_UTF32
14543
14544
#if SIMDUTF_FEATURE_UTF16
14545
  /**
14546
   * Count the number of code points (characters) in the string assuming that
14547
   * it is valid.
14548
   *
14549
   * This function assumes that the input string is valid UTF-16LE.
14550
   * It is acceptable to pass invalid UTF-16 strings but in such cases
14551
   * the result is implementation defined.
14552
   *
14553
   * This function is not BOM-aware.
14554
   *
14555
   * @param input         the UTF-16LE string to process
14556
   * @param length        the length of the string in 2-byte code units
14557
   * (char16_t)
14558
   * @return number of code points
14559
   */
14560
  simdutf_warn_unused virtual size_t
14561
  count_utf16le(const char16_t *input, size_t length) const noexcept = 0;
14562
14563
  /**
14564
   * Count the number of code points (characters) in the string assuming that
14565
   * it is valid.
14566
   *
14567
   * This function assumes that the input string is valid UTF-16BE.
14568
   * It is acceptable to pass invalid UTF-16 strings but in such cases
14569
   * the result is implementation defined.
14570
   *
14571
   * This function is not BOM-aware.
14572
   *
14573
   * @param input         the UTF-16BE string to process
14574
   * @param length        the length of the string in 2-byte code units
14575
   * (char16_t)
14576
   * @return number of code points
14577
   */
14578
  simdutf_warn_unused virtual size_t
14579
  count_utf16be(const char16_t *input, size_t length) const noexcept = 0;
14580
#endif // SIMDUTF_FEATURE_UTF16
14581
14582
#if SIMDUTF_FEATURE_UTF8
14583
  /**
14584
   * Count the number of code points (characters) in the string assuming that
14585
   * it is valid.
14586
   *
14587
   * This function assumes that the input string is valid UTF-8.
14588
   * It is acceptable to pass invalid UTF-8 strings but in such cases
14589
   * the result is implementation defined.
14590
   *
14591
   * @param input         the UTF-8 string to process
14592
   * @param length        the length of the string in bytes
14593
   * @return number of code points
14594
   */
14595
  simdutf_warn_unused virtual size_t
14596
  count_utf8(const char *input, size_t length) const noexcept = 0;
14597
#endif // SIMDUTF_FEATURE_UTF8
14598
14599
#if SIMDUTF_FEATURE_BASE64
14600
  /**
14601
   * Provide the maximal binary length in bytes given the base64 input.
14602
   * As long as the input does not contain ignorable characters (e.g., ASCII
14603
   * spaces or linefeed characters), the result is exact. In particular, the
14604
   * function checks for padding characters.
14605
   *
14606
   * The function is fast (constant time). It checks up to two characters at
14607
   * the end of the string. The input is not otherwise validated or read..
14608
   *
14609
   * @param input         the base64 input to process
14610
   * @param length        the length of the base64 input in bytes
14611
   * @return maximal number of binary bytes
14612
   */
14613
  simdutf_warn_unused size_t maximal_binary_length_from_base64(
14614
      const char *input, size_t length) const noexcept;
14615
14616
  /**
14617
   * Provide the maximal binary length in bytes given the base64 input.
14618
   * As long as the input does not contain ignorable characters (e.g., ASCII
14619
   * spaces or linefeed characters), the result is exact. In particular, the
14620
   * function checks for padding characters.
14621
   *
14622
   * The function is fast (constant time). It checks up to two characters at
14623
   * the end of the string. The input is not otherwise validated or read.
14624
   *
14625
   * @param input         the base64 input to process, in ASCII stored as 16-bit
14626
   * units
14627
   * @param length        the length of the base64 input in 16-bit units
14628
   * @return maximal number of binary bytes
14629
   */
14630
  simdutf_warn_unused size_t maximal_binary_length_from_base64(
14631
      const char16_t *input, size_t length) const noexcept;
14632
14633
  /**
14634
   * Compute the binary length from a base64 input with ASCII spaces.
14635
   * This function is useful for well-formed base64 inputs that may contain
14636
   * ASCII spaces (such as line breaks). For such inputs, the result is exact.
14637
   *
14638
   * The function counts non-whitespace characters (ASCII value > 0x20) and
14639
   * subtracts padding characters ('=') found at the end.
14640
   *
14641
   * @param input         the base64 input to process
14642
   * @param length        the length of the base64 input in bytes
14643
   * @return number of binary bytes
14644
   */
14645
  simdutf_warn_unused virtual size_t
14646
  binary_length_from_base64(const char *input, size_t length) const noexcept;
14647
14648
  /**
14649
   * Compute the binary length from a base64 input with ASCII spaces.
14650
   * This function is useful for well-formed base64 inputs that may contain
14651
   * ASCII spaces (such as line breaks). For such inputs, the result is exact.
14652
   *
14653
   * The function counts non-whitespace characters (ASCII value > 0x20) and
14654
   * subtracts padding characters ('=') found at the end.
14655
   *
14656
   * @param input         the base64 input to process, in ASCII stored as 16-bit
14657
   * units
14658
   * @param length        the length of the base64 input in 16-bit units
14659
   * @return number of binary bytes
14660
   */
14661
  simdutf_warn_unused virtual size_t
14662
  binary_length_from_base64(const char16_t *input,
14663
                            size_t length) const noexcept;
14664
14665
  /**
14666
   * Convert a base64 input to a binary output.
14667
   *
14668
   * This function follows the WHATWG forgiving-base64 format, which means that
14669
   * it will ignore any ASCII spaces in the input. You may provide a padded
14670
   * input (with one or two equal signs at the end) or an unpadded input
14671
   * (without any equal signs at the end).
14672
   *
14673
   * See https://infra.spec.whatwg.org/#forgiving-base64-decode
14674
   *
14675
   * This function will fail in case of invalid input. When last_chunk_options =
14676
   * loose, there are two possible reasons for failure: the input contains a
14677
   * number of base64 characters that when divided by 4, leaves a single
14678
   * remainder character (BASE64_INPUT_REMAINDER), or the input contains a
14679
   * character that is not a valid base64 character (INVALID_BASE64_CHARACTER).
14680
   *
14681
   * You should call this function with a buffer that is at least
14682
   * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
14683
   * provide that much space, the function may cause a buffer overflow.
14684
   *
14685
   * @param input         the base64 string to process
14686
   * @param length        the length of the string in bytes
14687
   * @param output        the pointer to a buffer that can hold the conversion
14688
   * result (should be at least maximal_binary_length_from_base64(input, length)
14689
   * bytes long).
14690
   * @param options       the base64 options to use, can be base64_default or
14691
   * base64_url, is base64_default by default.
14692
   * @param last_chunk_options the handling of the last chunk (default: loose)
14693
   * @return a result pair struct (of type simdutf::result containing the two
14694
   * fields error and count) with an error code and either position of the error
14695
   * (in the input in bytes) if any, or the number of bytes written if
14696
   * successful.
14697
   */
14698
  simdutf_warn_unused virtual result
14699
  base64_to_binary(const char *input, size_t length, char *output,
14700
                   base64_options options = base64_default,
14701
                   last_chunk_handling_options last_chunk_options =
14702
                       last_chunk_handling_options::loose) const noexcept = 0;
14703
14704
  /**
14705
   * Convert a base64 input to a binary output while returning more details
14706
   * than base64_to_binary.
14707
   *
14708
   * This function follows the WHATWG forgiving-base64 format, which means that
14709
   * it will ignore any ASCII spaces in the input. You may provide a padded
14710
   * input (with one or two equal signs at the end) or an unpadded input
14711
   * (without any equal signs at the end).
14712
   *
14713
   * See https://infra.spec.whatwg.org/#forgiving-base64-decode
14714
   *
14715
   * This function will fail in case of invalid input. When last_chunk_options =
14716
   * loose, there are two possible reasons for failure: the input contains a
14717
   * number of base64 characters that when divided by 4, leaves a single
14718
   * remainder character (BASE64_INPUT_REMAINDER), or the input contains a
14719
   * character that is not a valid base64 character (INVALID_BASE64_CHARACTER).
14720
   *
14721
   * You should call this function with a buffer that is at least
14722
   * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
14723
   * provide that much space, the function may cause a buffer overflow.
14724
   *
14725
   * @param input         the base64 string to process
14726
   * @param length        the length of the string in bytes
14727
   * @param output        the pointer to a buffer that can hold the conversion
14728
   * result (should be at least maximal_binary_length_from_base64(input, length)
14729
   * bytes long).
14730
   * @param options       the base64 options to use, can be base64_default or
14731
   * base64_url, is base64_default by default.
14732
   * @param last_chunk_options the handling of the last chunk (default: loose)
14733
   * @return a full_result pair struct (of type simdutf::result containing the
14734
   * three fields error, input_count and output_count).
14735
   */
14736
  simdutf_warn_unused virtual full_result base64_to_binary_details(
14737
      const char *input, size_t length, char *output,
14738
      base64_options options = base64_default,
14739
      last_chunk_handling_options last_chunk_options =
14740
          last_chunk_handling_options::loose) const noexcept = 0;
14741
14742
  /**
14743
   * Convert a base64 input to a binary output.
14744
   *
14745
   * This function follows the WHATWG forgiving-base64 format, which means that
14746
   * it will ignore any ASCII spaces in the input. You may provide a padded
14747
   * input (with one or two equal signs at the end) or an unpadded input
14748
   * (without any equal signs at the end).
14749
   *
14750
   * See https://infra.spec.whatwg.org/#forgiving-base64-decode
14751
   *
14752
   * This function will fail in case of invalid input. When last_chunk_options =
14753
   * loose, there are two possible reasons for failure: the input contains a
14754
   * number of base64 characters that when divided by 4, leaves a single
14755
   * remainder character (BASE64_INPUT_REMAINDER), or the input contains a
14756
   * character that is not a valid base64 character (INVALID_BASE64_CHARACTER).
14757
   *
14758
   * You should call this function with a buffer that is at least
14759
   * maximal_binary_length_from_base64(input, length) bytes long. If you
14760
   * fail to provide that much space, the function may cause a buffer overflow.
14761
   *
14762
   * @param input         the base64 string to process, in ASCII stored as
14763
   * 16-bit units
14764
   * @param length        the length of the string in 16-bit units
14765
   * @param output        the pointer to a buffer that can hold the conversion
14766
   * result (should be at least maximal_binary_length_from_base64(input, length)
14767
   * bytes long).
14768
   * @param options       the base64 options to use, can be base64_default or
14769
   * base64_url, is base64_default by default.
14770
   * @param last_chunk_options the handling of the last chunk (default: loose)
14771
   * @return a result pair struct (of type simdutf::result containing the two
14772
   * fields error and count) with an error code and position of the
14773
   * INVALID_BASE64_CHARACTER error (in the input in units) if any, or the
14774
   * number of bytes written if successful.
14775
   */
14776
  simdutf_warn_unused virtual result
14777
  base64_to_binary(const char16_t *input, size_t length, char *output,
14778
                   base64_options options = base64_default,
14779
                   last_chunk_handling_options last_chunk_options =
14780
                       last_chunk_handling_options::loose) const noexcept = 0;
14781
14782
  /**
14783
   * Convert a base64 input to a binary output while returning more details
14784
   * than base64_to_binary.
14785
   *
14786
   * This function follows the WHATWG forgiving-base64 format, which means that
14787
   * it will ignore any ASCII spaces in the input. You may provide a padded
14788
   * input (with one or two equal signs at the end) or an unpadded input
14789
   * (without any equal signs at the end).
14790
   *
14791
   * See https://infra.spec.whatwg.org/#forgiving-base64-decode
14792
   *
14793
   * This function will fail in case of invalid input. When last_chunk_options =
14794
   * loose, there are two possible reasons for failure: the input contains a
14795
   * number of base64 characters that when divided by 4, leaves a single
14796
   * remainder character (BASE64_INPUT_REMAINDER), or the input contains a
14797
   * character that is not a valid base64 character (INVALID_BASE64_CHARACTER).
14798
   *
14799
   * You should call this function with a buffer that is at least
14800
   * maximal_binary_length_from_base64(input, length) bytes long. If you fail to
14801
   * provide that much space, the function may cause a buffer overflow.
14802
   *
14803
   * @param input         the base64 string to process
14804
   * @param length        the length of the string in bytes
14805
   * @param output        the pointer to a buffer that can hold the conversion
14806
   * result (should be at least maximal_binary_length_from_base64(input, length)
14807
   * bytes long).
14808
   * @param options       the base64 options to use, can be base64_default or
14809
   * base64_url, is base64_default by default.
14810
   * @param last_chunk_options the handling of the last chunk (default: loose)
14811
   * @return a full_result pair struct (of type simdutf::result containing the
14812
   * three fields error, input_count and output_count).
14813
   */
14814
  simdutf_warn_unused virtual full_result base64_to_binary_details(
14815
      const char16_t *input, size_t length, char *output,
14816
      base64_options options = base64_default,
14817
      last_chunk_handling_options last_chunk_options =
14818
          last_chunk_handling_options::loose) const noexcept = 0;
14819
14820
  /**
14821
   * Provide the base64 length in bytes given the length of a binary input.
14822
   *
14823
   * @param length        the length of the input in bytes
14824
   * @param options       the base64 options to use, can be base64_default or
14825
   * base64_url, is base64_default by default.
14826
   * @return number of base64 bytes
14827
   */
14828
  simdutf_warn_unused size_t base64_length_from_binary(
14829
      size_t length, base64_options options = base64_default) const noexcept;
14830
14831
  /**
14832
   * Convert a binary input to a base64 output.
14833
   *
14834
   * The default option (simdutf::base64_default) uses the characters `+` and
14835
   * `/` as part of its alphabet. Further, it adds padding (`=`) at the end of
14836
   * the output to ensure that the output length is a multiple of four.
14837
   *
14838
   * The URL option (simdutf::base64_url) uses the characters `-` and `_` as
14839
   * part of its alphabet. No padding is added at the end of the output.
14840
   *
14841
   * This function always succeeds.
14842
   *
14843
   * @param input         the binary to process
14844
   * @param length        the length of the input in bytes
14845
   * @param output        the pointer to a buffer that can hold the conversion
14846
   * result (should be at least base64_length_from_binary(length) bytes long)
14847
   * @param options       the base64 options to use, can be base64_default or
14848
   * base64_url, is base64_default by default.
14849
   * @return number of written bytes, will be equal to
14850
   * base64_length_from_binary(length, options)
14851
   */
14852
  virtual size_t
14853
  binary_to_base64(const char *input, size_t length, char *output,
14854
                   base64_options options = base64_default) const noexcept = 0;
14855
14856
  /**
14857
   * Convert a binary input to a base64 output with lines of given length.
14858
   * Lines are separated by a single linefeed character.
14859
   *
14860
   * The default option (simdutf::base64_default) uses the characters `+` and
14861
   * `/` as part of its alphabet. Further, it adds padding (`=`) at the end of
14862
   * the output to ensure that the output length is a multiple of four.
14863
   *
14864
   * The URL option (simdutf::base64_url) uses the characters `-` and `_` as
14865
   * part of its alphabet. No padding is added at the end of the output.
14866
   *
14867
   * This function always succeeds.
14868
   *
14869
   * @param input         the binary to process
14870
   * @param length        the length of the input in bytes
14871
   * @param output        the pointer to a buffer that can hold the conversion
14872
   * result (should be at least base64_length_from_binary_with_lines(length,
14873
   * options, line_length) bytes long)
14874
   * @param line_length   the length of each line, values smaller than 4 are
14875
   * interpreted as 4
14876
   * @param options       the base64 options to use, can be base64_default or
14877
   * base64_url, is base64_default by default.
14878
   * @return number of written bytes, will be equal to
14879
   * base64_length_from_binary_with_lines(length, options, line_length)
14880
   */
14881
  virtual size_t binary_to_base64_with_lines(
14882
      const char *input, size_t length, char *output,
14883
      size_t line_length = simdutf::default_line_length,
14884
      base64_options options = base64_default) const noexcept = 0;
14885
14886
  /**
14887
   * Find the first occurrence of a character in a string. If the character is
14888
   * not found, return a pointer to the end of the string.
14889
   * @param start        the start of the string
14890
   * @param end          the end of the string
14891
   * @param character    the character to find
14892
   * @return a pointer to the first occurrence of the character in the string,
14893
   * or a pointer to the end of the string if the character is not found.
14894
   *
14895
   */
14896
  virtual const char *find(const char *start, const char *end,
14897
                           char character) const noexcept = 0;
14898
  virtual const char16_t *find(const char16_t *start, const char16_t *end,
14899
                               char16_t character) const noexcept = 0;
14900
#endif // SIMDUTF_FEATURE_BASE64
14901
14902
#ifdef SIMDUTF_INTERNAL_TESTS
14903
  // This method is exported only in developer mode, its purpose
14904
  // is to expose some internal test procedures from the given
14905
  // implementation and then use them through our standard test
14906
  // framework.
14907
  //
14908
  // Regular users should not use it, the tests of the public
14909
  // API are enough.
14910
14911
  struct TestProcedure {
14912
    // display name
14913
    std::string_view name;
14914
14915
    // procedure should return whether given test pass or not
14916
    void (*procedure)(const implementation &);
14917
  };
14918
14919
  virtual std::vector<TestProcedure> internal_tests() const;
14920
#endif
14921
14922
protected:
14923
  /** @private Construct an implementation with the given name and description.
14924
   * For subclasses.
14925
   * @param name the name of this implementation
14926
   * @param description a description of this implementation
14927
   * @param required_instruction_sets the instruction sets this implementation
14928
   * requires
14929
   */
14930
  simdutf_really_inline implementation(const char *name,
14931
                                       const char *description,
14932
                                       uint32_t required_instruction_sets)
14933
5
      : _name(name), _description(description),
14934
5
        _required_instruction_sets(required_instruction_sets) {}
14935
14936
protected:
14937
  ~implementation() = default;
14938
14939
private:
14940
  /**
14941
   * The name of this implementation.
14942
   */
14943
  const char *_name;
14944
14945
  /**
14946
   * The description of this implementation.
14947
   */
14948
  const char *_description;
14949
14950
  /**
14951
   * Instruction sets required for this implementation.
14952
   */
14953
  const uint32_t _required_instruction_sets;
14954
};
14955
14956
/** @private */
14957
namespace internal {
14958
14959
/**
14960
 * The list of available implementations compiled into simdutf.
14961
 */
14962
class available_implementation_list {
14963
public:
14964
  /** Get the list of available implementations compiled into simdutf */
14965
1
  simdutf_really_inline available_implementation_list() {}
14966
  /** Number of implementations */
14967
  size_t size() const noexcept;
14968
  /** STL const begin() iterator */
14969
  const implementation *const *begin() const noexcept;
14970
  /** STL const end() iterator */
14971
  const implementation *const *end() const noexcept;
14972
14973
  /**
14974
   * Get the implementation with the given name.
14975
   *
14976
   * Case sensitive.
14977
   *
14978
   *     const implementation *impl =
14979
   * simdutf::available_implementations["westmere"]; if (!impl) { exit(1); } if
14980
   * (!imp->supported_by_runtime_system()) { exit(1); }
14981
   *     simdutf::active_implementation = impl;
14982
   *
14983
   * @param name the implementation to find, e.g. "westmere", "haswell", "arm64"
14984
   * @return the implementation, or nullptr if the parse failed.
14985
   */
14986
0
  const implementation *operator[](std::string_view name) const noexcept {
14987
0
    for (const implementation *impl : *this) {
14988
0
      if (impl->name() == name) {
14989
0
        return impl;
14990
0
      }
14991
0
    }
14992
0
    return nullptr;
14993
0
  }
14994
14995
  /**
14996
   * Detect the most advanced implementation supported by the current host.
14997
   *
14998
   * This is used to initialize the implementation on startup.
14999
   *
15000
   *     const implementation *impl =
15001
   * simdutf::available_implementation::detect_best_supported();
15002
   *     simdutf::active_implementation = impl;
15003
   *
15004
   * @return the most advanced supported implementation for the current host, or
15005
   * an implementation that returns UNSUPPORTED_ARCHITECTURE if there is no
15006
   * supported implementation. Will never return nullptr.
15007
   */
15008
  const implementation *detect_best_supported() const noexcept;
15009
};
15010
15011
template <typename T> class atomic_ptr {
15012
public:
15013
1
  atomic_ptr(T *_ptr) : ptr{_ptr} {}
15014
15015
#if defined(SIMDUTF_NO_THREADS)
15016
  operator const T *() const { return ptr; }
15017
  const T &operator*() const { return *ptr; }
15018
  const T *operator->() const { return ptr; }
15019
15020
  operator T *() { return ptr; }
15021
  T &operator*() { return *ptr; }
15022
  T *operator->() { return ptr; }
15023
  atomic_ptr &operator=(T *_ptr) {
15024
    ptr = _ptr;
15025
    return *this;
15026
  }
15027
15028
#else
15029
  operator const T *() const { return ptr.load(); }
15030
  const T &operator*() const { return *ptr; }
15031
  const T *operator->() const { return ptr.load(); }
15032
15033
1
  operator T *() { return ptr.load(); }
15034
  T &operator*() { return *ptr; }
15035
22.7k
  T *operator->() { return ptr.load(); }
15036
1
  atomic_ptr &operator=(T *_ptr) {
15037
1
    ptr = _ptr;
15038
1
    return *this;
15039
1
  }
15040
15041
#endif
15042
15043
private:
15044
#if defined(SIMDUTF_NO_THREADS)
15045
  T *ptr;
15046
#else
15047
  std::atomic<T *> ptr;
15048
#endif
15049
};
15050
15051
class detect_best_supported_implementation_on_first_use;
15052
15053
} // namespace internal
15054
15055
/**
15056
 * The list of available implementations compiled into simdutf.
15057
 */
15058
extern SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list &
15059
get_available_implementations();
15060
15061
/**
15062
 * The active implementation.
15063
 *
15064
 * Automatically initialized on first use to the most advanced implementation
15065
 * supported by this hardware.
15066
 */
15067
extern SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr<const implementation> &
15068
get_active_implementation();
15069
15070
} // namespace simdutf
15071
15072
#if SIMDUTF_FEATURE_BASE64
15073
  // this header is not part of the public api
15074
/* begin file include/simdutf/base64_implementation.h */
15075
#ifndef SIMDUTF_BASE64_IMPLEMENTATION_H
15076
#define SIMDUTF_BASE64_IMPLEMENTATION_H
15077
15078
// this is not part of the public api
15079
15080
#include <type_traits> // for is_same
15081
15082
namespace simdutf {
15083
15084
template <typename chartype>
15085
simdutf_warn_unused simdutf_constexpr23 result slow_base64_to_binary_safe_impl(
15086
    const chartype *input, size_t length, char *output, size_t &outlen,
15087
    base64_options options,
15088
0
    last_chunk_handling_options last_chunk_options) noexcept {
15089
0
  const bool ignore_garbage = (options & base64_default_accept_garbage) != 0;
15090
0
  auto ri = simdutf::scalar::base64::find_end(input, length, options);
15091
0
  size_t equallocation = ri.equallocation;
15092
0
  size_t equalsigns = ri.equalsigns;
15093
0
  length = ri.srclen;
15094
0
  size_t full_input_length = ri.full_input_length;
15095
0
  (void)full_input_length;
15096
0
  if (length == 0) {
15097
0
    outlen = 0;
15098
0
    if (!ignore_garbage && equalsigns > 0) {
15099
0
      return {INVALID_BASE64_CHARACTER, equallocation};
15100
0
    }
15101
0
    return {SUCCESS, 0};
15102
0
  }
15103
15104
  // The parameters of base64_tail_decode_safe are:
15105
  // - dst: the output buffer
15106
  // - outlen: the size of the output buffer
15107
  // - srcr: the input buffer
15108
  // - length: the size of the input buffer
15109
  // - padded_characters: the number of padding characters
15110
  // - options: the options for the base64 decoder
15111
  // - last_chunk_options: the options for the last chunk
15112
  // The function will return the number of bytes written to the output buffer
15113
  // and the number of bytes read from the input buffer.
15114
  // The function will also return an error code if the input buffer is not
15115
  // valid base64.
15116
0
  full_result r = scalar::base64::base64_tail_decode_safe(
15117
0
      output, outlen, input, length, equalsigns, options, last_chunk_options);
15118
0
  r = scalar::base64::patch_tail_result(r, 0, 0, equallocation,
15119
0
                                        full_input_length, last_chunk_options);
15120
0
  outlen = r.output_count;
15121
0
  if (!is_partial(last_chunk_options) && r.error == error_code::SUCCESS &&
15122
0
      equalsigns > 0) {
15123
    // additional checks
15124
0
    if ((outlen % 3 == 0) || ((outlen % 3) + 1 + equalsigns != 4)) {
15125
0
      r.error = error_code::INVALID_BASE64_CHARACTER;
15126
0
    }
15127
0
  }
15128
0
  return {r.error, r.input_count}; // we cannot return r itself because it gets
15129
                                   // converted to error/output_count
15130
0
}
Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options)
Unexecuted instantiation: simdutf::result simdutf::slow_base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options)
15131
15132
template <typename chartype>
15133
simdutf_warn_unused simdutf_constexpr23 result base64_to_binary_safe_impl(
15134
    const chartype *input, size_t length, char *output, size_t &outlen,
15135
    base64_options options,
15136
    last_chunk_handling_options last_chunk_handling_options,
15137
0
    bool decode_up_to_bad_char) noexcept {
15138
0
  static_assert(std::is_same<chartype, char>::value ||
15139
0
                    std::is_same<chartype, char16_t>::value,
15140
0
                "Only char and char16_t are supported.");
15141
0
  size_t remaining_input_length = length;
15142
0
  size_t remaining_output_length = outlen;
15143
0
  size_t input_position = 0;
15144
0
  size_t output_position = 0;
15145
15146
  // We also do a first pass using the fast path to decode as much as possible
15147
0
  size_t safe_input = detail::min(
15148
0
      remaining_input_length,
15149
0
      base64_length_from_binary(remaining_output_length / 3 * 3, options));
15150
0
  bool done_with_partial = (safe_input == remaining_input_length);
15151
0
  simdutf::full_result r;
15152
15153
#if SIMDUTF_CPLUSPLUS23
15154
  if consteval {
15155
    r = scalar::base64::base64_to_binary_details_impl(
15156
        input + input_position, safe_input, output + output_position, options,
15157
        done_with_partial
15158
            ? last_chunk_handling_options
15159
            : simdutf::last_chunk_handling_options::only_full_chunks);
15160
  } else
15161
#endif
15162
0
  {
15163
0
    r = get_active_implementation()->base64_to_binary_details(
15164
0
        input + input_position, safe_input, output + output_position, options,
15165
0
        done_with_partial
15166
0
            ? last_chunk_handling_options
15167
0
            : simdutf::last_chunk_handling_options::only_full_chunks);
15168
0
  }
15169
0
  simdutf_log_assert(r.input_count <= safe_input,
15170
0
                     "You should not read more than safe_input");
15171
0
  simdutf_log_assert(r.output_count <= remaining_output_length,
15172
0
                     "You should not write more than remaining_output_length");
15173
  // Technically redundant, but we want to be explicit about it.
15174
0
  input_position += r.input_count;
15175
0
  output_position += r.output_count;
15176
0
  remaining_input_length -= r.input_count;
15177
0
  remaining_output_length -= r.output_count;
15178
0
  if (r.error != simdutf::error_code::SUCCESS) {
15179
    // There is an error. We return.
15180
0
    if (decode_up_to_bad_char &&
15181
0
        r.error == error_code::INVALID_BASE64_CHARACTER) {
15182
0
      return slow_base64_to_binary_safe_impl(
15183
0
          input, length, output, outlen, options, last_chunk_handling_options);
15184
0
    }
15185
0
    outlen = output_position;
15186
0
    return {r.error, input_position};
15187
0
  }
15188
15189
0
  if (done_with_partial) {
15190
    // We are done. We have decoded everything.
15191
0
    outlen = output_position;
15192
0
    return {simdutf::error_code::SUCCESS, input_position};
15193
0
  }
15194
  // We have decoded some data, but we still have some data to decode.
15195
  // We need to decode the rest of the input buffer.
15196
0
  r = simdutf::scalar::base64::base64_to_binary_details_safe_impl(
15197
0
      input + input_position, remaining_input_length, output + output_position,
15198
0
      remaining_output_length, options, last_chunk_handling_options);
15199
0
  input_position += r.input_count;
15200
0
  output_position += r.output_count;
15201
0
  remaining_input_length -= r.input_count;
15202
0
  remaining_output_length -= r.output_count;
15203
15204
0
  if (r.error != simdutf::error_code::SUCCESS) {
15205
    // There is an error. We return.
15206
0
    if (decode_up_to_bad_char &&
15207
0
        r.error == error_code::INVALID_BASE64_CHARACTER) {
15208
0
      return slow_base64_to_binary_safe_impl(
15209
0
          input, length, output, outlen, options, last_chunk_handling_options);
15210
0
    }
15211
0
    outlen = output_position;
15212
0
    return {r.error, input_position};
15213
0
  }
15214
0
  if (input_position < length) {
15215
    // We cannot process the entire input in one go, so we need to
15216
    // process it in two steps: first the fast path, then the slow path.
15217
    // In some cases, the processing might 'eat up' trailing ignorable
15218
    // characters in the fast path, but that can be a problem.
15219
    // suppose we have just white space followed by a single base64 character.
15220
    // If we first process the white space with the fast path, it will
15221
    // eat all of it. But, by the JavaScript standard, we should consume
15222
    // no character. See
15223
    // https://tc39.es/proposal-arraybuffer-base64/spec/#sec-frombase64
15224
0
    while (input_position > 0 &&
15225
0
           base64_ignorable(input[input_position - 1], options)) {
15226
0
      input_position--;
15227
0
    }
15228
0
  }
15229
0
  outlen = output_position;
15230
0
  return {simdutf::error_code::SUCCESS, input_position};
15231
0
}
Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char>(char const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool)
Unexecuted instantiation: simdutf::result simdutf::base64_to_binary_safe_impl<char16_t>(char16_t const*, unsigned long, char*, unsigned long&, simdutf::base64_options, simdutf::last_chunk_handling_options, bool)
15232
15233
} // namespace simdutf
15234
#endif // SIMDUTF_BASE64_IMPLEMENTATION_H
15235
/* end file include/simdutf/base64_implementation.h */
15236
15237
namespace simdutf {
15238
  #if SIMDUTF_SPAN
15239
/**
15240
 * @brief span overload
15241
 * @return a tuple of result and outlen
15242
 */
15243
simdutf_really_inline
15244
    simdutf_constexpr23 simdutf_warn_unused std::tuple<result, std::size_t>
15245
    base64_to_binary_safe(
15246
        const detail::input_span_of_byte_like auto &input,
15247
        detail::output_span_of_byte_like auto &&binary_output,
15248
        base64_options options = base64_default,
15249
        last_chunk_handling_options last_chunk_options = loose,
15250
        bool decode_up_to_bad_char = false) noexcept {
15251
  size_t outlen = binary_output.size();
15252
    #if SIMDUTF_CPLUSPLUS23
15253
  if consteval {
15254
    using CInput = std::decay_t<decltype(*input.data())>;
15255
    static_assert(std::is_same_v<CInput, char>,
15256
                  "sorry, the constexpr implementation is for now limited to "
15257
                  "input of type char");
15258
    using COutput = std::decay_t<decltype(*binary_output.data())>;
15259
    static_assert(std::is_same_v<COutput, char>,
15260
                  "sorry, the constexpr implementation is for now limited to "
15261
                  "output of type char");
15262
    auto r = base64_to_binary_safe_impl(
15263
        input.data(), input.size(), binary_output.data(), outlen, options,
15264
        last_chunk_options, decode_up_to_bad_char);
15265
    return {r, outlen};
15266
  } else
15267
    #endif
15268
  {
15269
    auto r = base64_to_binary_safe_impl<char>(
15270
        reinterpret_cast<const char *>(input.data()), input.size(),
15271
        reinterpret_cast<char *>(binary_output.data()), outlen, options,
15272
        last_chunk_options, decode_up_to_bad_char);
15273
    return {r, outlen};
15274
  }
15275
}
15276
15277
    #if SIMDUTF_SPAN
15278
/**
15279
 * @brief span overload
15280
 * @return a tuple of result and outlen
15281
 */
15282
simdutf_really_inline
15283
    simdutf_warn_unused simdutf_constexpr23 std::tuple<result, std::size_t>
15284
    base64_to_binary_safe(
15285
        std::span<const char16_t> input,
15286
        detail::output_span_of_byte_like auto &&binary_output,
15287
        base64_options options = base64_default,
15288
        last_chunk_handling_options last_chunk_options = loose,
15289
        bool decode_up_to_bad_char = false) noexcept {
15290
  size_t outlen = binary_output.size();
15291
      #if SIMDUTF_CPLUSPLUS23
15292
  if consteval {
15293
    auto r = base64_to_binary_safe_impl(
15294
        input.data(), input.size(), binary_output.data(), outlen, options,
15295
        last_chunk_options, decode_up_to_bad_char);
15296
    return {r, outlen};
15297
  } else
15298
      #endif
15299
  {
15300
    auto r = base64_to_binary_safe(
15301
        input.data(), input.size(),
15302
        reinterpret_cast<char *>(binary_output.data()), outlen, options,
15303
        last_chunk_options, decode_up_to_bad_char);
15304
    return {r, outlen};
15305
  }
15306
}
15307
    #endif // SIMDUTF_SPAN
15308
15309
  #endif // SIMDUTF_SPAN
15310
} // namespace simdutf
15311
15312
#endif // SIMDUTF_FEATURE_BASE64
15313
15314
#if SIMDUTF_CPLUSPLUS23 && SIMDUTF_FEATURE_BASE64
15315
15316
namespace simdutf {
15317
namespace literals {
15318
15319
namespace detail {
15320
15321
// the detail namespace is not part of the public api
15322
15323
template <std::size_t N> struct base64_literal_helper {
15324
  std::array<char, N - 1> storage{};
15325
  static constexpr std::size_t size() noexcept { return N - 1; }
15326
  consteval base64_literal_helper(const char (&str)[N]) {
15327
    for (std::size_t i = 0; i < size(); i++) {
15328
      storage[i] = str[i];
15329
    }
15330
  }
15331
};
15332
15333
template <std::size_t InputLen> struct base64_decode_result {
15334
  static constexpr std::size_t max_out = (InputLen + 3) / 4 * 3;
15335
  std::array<char, max_out> buffer{};
15336
  std::size_t output_count{};
15337
};
15338
15339
template <std::size_t InputLen>
15340
consteval auto base64_decode_literal(const char *str) {
15341
  base64_decode_result<InputLen> result{};
15342
  auto r = scalar::base64::base64_to_binary_details_impl(
15343
      str, InputLen, result.buffer.data(), base64_default, loose);
15344
  if (r.error != error_code::SUCCESS) {
15345
  #if __cpp_lib_unreachable >= 202202L
15346
    std::unreachable(); // invalid base64 input in _base64 literal
15347
  #else
15348
    // workaround for older stdlib
15349
    throw "invalid base64 input in _base64 literal";
15350
  #endif
15351
  }
15352
  result.output_count = r.output_count;
15353
  return result;
15354
}
15355
15356
template <base64_literal_helper a> consteval auto base64_make_array() {
15357
  constexpr auto decoded = base64_decode_literal<a.size()>(a.storage.data());
15358
  std::array<char, decoded.output_count> ret{};
15359
  for (std::size_t i = 0; i < decoded.output_count; i++) {
15360
    ret[i] = decoded.buffer[i];
15361
  }
15362
  return ret;
15363
}
15364
15365
} // namespace detail
15366
15367
/**
15368
 * User-defined literal for compile-time base64 decoding.
15369
 *
15370
 * Usage:
15371
 *   using namespace simdutf::literals;
15372
 *   constexpr auto decoded = "SGVsbG8gV29ybGQh"_base64;
15373
 *   // decoded is a std::array<char, 12> containing "Hello World!"
15374
 *
15375
 * The input must be valid base64. Whitepace is allowed and ignored.
15376
 * A compilation error occurs if the input is invalid.
15377
 */
15378
template <detail::base64_literal_helper a> consteval auto operator""_base64() {
15379
  return detail::base64_make_array<a>();
15380
}
15381
15382
} // namespace literals
15383
} // namespace simdutf
15384
15385
#endif // SIMDUTF_CPLUSPLUS23 && SIMDUTF_FEATURE_BASE64
15386
15387
#endif // SIMDUTF_IMPLEMENTATION_H
15388
/* end file include/simdutf/implementation.h */
15389
15390
// Implementation-internal files (must be included before the implementations
15391
// themselves, to keep amalgamation working--otherwise, the first time a file is
15392
// included, it might be put inside the #ifdef
15393
// SIMDUTF_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other
15394
// implementations can't compile unless that implementation is turned on).
15395
15396
SIMDUTF_POP_DISABLE_WARNINGS
15397
15398
#endif // SIMDUTF_H
15399
/* end file include/simdutf.h */