aboutsummaryrefslogtreecommitdiff
path: root/vendor/libghostty-vt/pkg
diff options
context:
space:
mode:
authorCan Celik <ogulcancelik@gmail.com>2026-04-04 19:22:04 +0300
committerCan Celik <ogulcancelik@gmail.com>2026-04-05 01:23:06 +0300
commit41dd733ca7123c9ac4bfc6c781697a660e95b052 (patch)
tree3fc91815ca139ef2e0b281bba0abda4ef15175f4 /vendor/libghostty-vt/pkg
parent55c138263e190a1e79d4b997bfb9397306c0ec1f (diff)
feat: finish ghostty pane backend integration
Diffstat (limited to 'vendor/libghostty-vt/pkg')
-rw-r--r--vendor/libghostty-vt/pkg/README.md33
-rw-r--r--vendor/libghostty-vt/pkg/afl++/LICENSE23
-rw-r--r--vendor/libghostty-vt/pkg/afl++/afl.c141
-rw-r--r--vendor/libghostty-vt/pkg/afl++/build.zig59
-rw-r--r--vendor/libghostty-vt/pkg/afl++/build.zig.zon11
-rw-r--r--vendor/libghostty-vt/pkg/android-ndk/build.zig207
-rw-r--r--vendor/libghostty-vt/pkg/android-ndk/build.zig.zon10
-rw-r--r--vendor/libghostty-vt/pkg/apple-sdk/build.zig111
-rw-r--r--vendor/libghostty-vt/pkg/apple-sdk/build.zig.zon7
-rw-r--r--vendor/libghostty-vt/pkg/breakpad/build.zig159
-rw-r--r--vendor/libghostty-vt/pkg/breakpad/build.zig.zon15
-rw-r--r--vendor/libghostty-vt/pkg/dcimgui/build.zig210
-rw-r--r--vendor/libghostty-vt/pkg/dcimgui/build.zig.zon26
-rw-r--r--vendor/libghostty-vt/pkg/dcimgui/ext.cpp53
-rw-r--r--vendor/libghostty-vt/pkg/dcimgui/main.zig77
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/build.zig298
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/build.zig.zon16
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/char_set.zig40
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/common.zig139
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/config.zig61
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/error.zig7
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/font_set.zig44
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/init.zig43
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/lang_set.zig61
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/main.zig45
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/matrix.zig10
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/object_set.zig30
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/override/include/fcalias.h0
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/override/include/fcaliastail.h0
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/override/include/fcftalias.h0
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/override/include/fcftaliastail.h0
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/override/include/fcobjshash.h347
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/pattern.zig168
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/range.zig13
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/test.zig66
-rw-r--r--vendor/libghostty-vt/pkg/fontconfig/value.zig76
-rw-r--r--vendor/libghostty-vt/pkg/freetype/Library.zig92
-rw-r--r--vendor/libghostty-vt/pkg/freetype/build.zig219
-rw-r--r--vendor/libghostty-vt/pkg/freetype/build.zig.zon18
-rw-r--r--vendor/libghostty-vt/pkg/freetype/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/freetype/computations.zig8
-rw-r--r--vendor/libghostty-vt/pkg/freetype/errors.zig295
-rw-r--r--vendor/libghostty-vt/pkg/freetype/face.zig385
-rw-r--r--vendor/libghostty-vt/pkg/freetype/freetype-zig.h9
-rw-r--r--vendor/libghostty-vt/pkg/freetype/main.zig18
-rwxr-xr-xvendor/libghostty-vt/pkg/freetype/res/FiraCode-Regular.ttfbin0 -> 289624 bytes
-rw-r--r--vendor/libghostty-vt/pkg/freetype/tag.zig17
-rw-r--r--vendor/libghostty-vt/pkg/freetype/test.zig1
-rw-r--r--vendor/libghostty-vt/pkg/glslang/build.zig169
-rw-r--r--vendor/libghostty-vt/pkg/glslang/build.zig.zon16
-rw-r--r--vendor/libghostty-vt/pkg/glslang/c.zig4
-rw-r--r--vendor/libghostty-vt/pkg/glslang/init.zig9
-rw-r--r--vendor/libghostty-vt/pkg/glslang/main.zig15
-rw-r--r--vendor/libghostty-vt/pkg/glslang/override/glslang/build_info.h62
-rw-r--r--vendor/libghostty-vt/pkg/glslang/program.zig60
-rw-r--r--vendor/libghostty-vt/pkg/glslang/shader.zig58
-rw-r--r--vendor/libghostty-vt/pkg/glslang/test.zig10
-rw-r--r--vendor/libghostty-vt/pkg/glslang/test/simple.frag56
-rw-r--r--vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig127
-rw-r--r--vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig.zon18
-rw-r--r--vendor/libghostty-vt/pkg/gtk4-layer-shell/src/main.zig71
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/blob.zig126
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/buffer.zig373
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/build.zig185
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon18
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/c.zig8
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/common.zig247
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/coretext.zig30
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/errors.zig5
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/face.zig17
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/font.zig28
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/freetype.zig78
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/main.zig31
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/shape.zig27
-rw-r--r--vendor/libghostty-vt/pkg/harfbuzz/version.zig47
-rw-r--r--vendor/libghostty-vt/pkg/highway/bridge.cpp9
-rw-r--r--vendor/libghostty-vt/pkg/highway/build.zig141
-rw-r--r--vendor/libghostty-vt/pkg/highway/build.zig.zon17
-rw-r--r--vendor/libghostty-vt/pkg/highway/main.zig57
-rw-r--r--vendor/libghostty-vt/pkg/libintl/build.zig104
-rw-r--r--vendor/libghostty-vt/pkg/libintl/build.zig.zon15
-rw-r--r--vendor/libghostty-vt/pkg/libintl/config.h2370
-rw-r--r--vendor/libghostty-vt/pkg/libintl/libgnuintl.h1
-rw-r--r--vendor/libghostty-vt/pkg/libintl/libintl.h1168
-rw-r--r--vendor/libghostty-vt/pkg/libpng/build.zig91
-rw-r--r--vendor/libghostty-vt/pkg/libpng/build.zig.zon17
-rw-r--r--vendor/libghostty-vt/pkg/libpng/pnglibconf.h219
-rw-r--r--vendor/libghostty-vt/pkg/libxml2/build.zig227
-rw-r--r--vendor/libghostty-vt/pkg/libxml2/build.zig.zon13
-rw-r--r--vendor/libghostty-vt/pkg/libxml2/override/config/posix/config.h80
-rw-r--r--vendor/libghostty-vt/pkg/libxml2/override/config/win32/config.h20
-rw-r--r--vendor/libghostty-vt/pkg/libxml2/override/include/libxml/xmlversion.h18
-rw-r--r--vendor/libghostty-vt/pkg/macos/animation.zig10
-rw-r--r--vendor/libghostty-vt/pkg/macos/animation/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/build.zig82
-rw-r--r--vendor/libghostty-vt/pkg/macos/build.zig.zon9
-rw-r--r--vendor/libghostty-vt/pkg/macos/carbon.zig5
-rw-r--r--vendor/libghostty-vt/pkg/macos/carbon/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/dispatch.zig18
-rw-r--r--vendor/libghostty-vt/pkg/macos/dispatch/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/dispatch/data.zig31
-rw-r--r--vendor/libghostty-vt/pkg/macos/dispatch/queue.zig8
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation.zig37
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/array.zig176
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig103
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/base.zig35
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/character_set.zig49
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/data.zig38
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig125
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/number.zig79
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/string.zig174
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/type.zig2
-rw-r--r--vendor/libghostty-vt/pkg/macos/foundation/url.zig104
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics.zig24
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/affine_transform.zig16
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/bitmap_context.zig50
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/color_space.zig99
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/context.zig172
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/font.zig3
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/geometry.zig34
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/image.zig31
-rw-r--r--vendor/libghostty-vt/pkg/macos/graphics/path.zig68
-rw-r--r--vendor/libghostty-vt/pkg/macos/iosurface.zig8
-rw-r--r--vendor/libghostty-vt/pkg/macos/iosurface/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/iosurface/iosurface.zig136
-rw-r--r--vendor/libghostty-vt/pkg/macos/main.zig35
-rw-r--r--vendor/libghostty-vt/pkg/macos/os.zig10
-rw-r--r--vendor/libghostty-vt/pkg/macos/os/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/os/log.zig65
-rw-r--r--vendor/libghostty-vt/pkg/macos/os/signpost.zig214
-rw-r--r--vendor/libghostty-vt/pkg/macos/os/zig_macos.c11
-rw-r--r--vendor/libghostty-vt/pkg/macos/text.zig38
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/ext.c10
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/font.zig306
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/font_collection.zig139
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/font_descriptor.zig287
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/font_manager.zig23
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/frame.zig33
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/framesetter.zig66
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/line.zig123
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/paragraph_style.zig49
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/run.zig117
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/stylized_strings.zig16
-rw-r--r--vendor/libghostty-vt/pkg/macos/text/typesetter.zig36
-rw-r--r--vendor/libghostty-vt/pkg/macos/video.zig10
-rw-r--r--vendor/libghostty-vt/pkg/macos/video/c.zig1
-rw-r--r--vendor/libghostty-vt/pkg/macos/video/display_link.zig86
-rw-r--r--vendor/libghostty-vt/pkg/macos/video/pixel_format.zig171
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/build.zig171
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/build.zig.zon16
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/errors.zig201
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/init.zig16
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/main.zig21
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/match_param.zig23
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/regex.zig152
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/region.zig51
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/testing.zig15
-rw-r--r--vendor/libghostty-vt/pkg/oniguruma/types.zig96
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Buffer.zig245
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Framebuffer.zig116
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Program.zig137
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Renderbuffer.zig56
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Sampler.zig43
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Shader.zig56
-rw-r--r--vendor/libghostty-vt/pkg/opengl/Texture.zig221
-rw-r--r--vendor/libghostty-vt/pkg/opengl/VertexArray.zig116
-rw-r--r--vendor/libghostty-vt/pkg/opengl/build.zig6
-rw-r--r--vendor/libghostty-vt/pkg/opengl/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/opengl/draw.zig94
-rw-r--r--vendor/libghostty-vt/pkg/opengl/errors.zig33
-rw-r--r--vendor/libghostty-vt/pkg/opengl/extensions.zig32
-rw-r--r--vendor/libghostty-vt/pkg/opengl/glad.zig45
-rw-r--r--vendor/libghostty-vt/pkg/opengl/main.zig45
-rw-r--r--vendor/libghostty-vt/pkg/opengl/primitives.zig18
-rw-r--r--vendor/libghostty-vt/pkg/sentry/build.zig243
-rw-r--r--vendor/libghostty-vt/pkg/sentry/build.zig.zon17
-rw-r--r--vendor/libghostty-vt/pkg/sentry/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/sentry/envelope.zig31
-rw-r--r--vendor/libghostty-vt/pkg/sentry/level.zig8
-rw-r--r--vendor/libghostty-vt/pkg/sentry/main.zig35
-rw-r--r--vendor/libghostty-vt/pkg/sentry/transport.zig26
-rw-r--r--vendor/libghostty-vt/pkg/sentry/uuid.zig22
-rw-r--r--vendor/libghostty-vt/pkg/sentry/value.zig75
-rw-r--r--vendor/libghostty-vt/pkg/simdutf/build.zig81
-rw-r--r--vendor/libghostty-vt/pkg/simdutf/build.zig.zon10
-rw-r--r--vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.cpp38579
-rw-r--r--vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.h3757
-rw-r--r--vendor/libghostty-vt/pkg/spirv-cross/build.zig120
-rw-r--r--vendor/libghostty-vt/pkg/spirv-cross/build.zig.zon16
-rw-r--r--vendor/libghostty-vt/pkg/spirv-cross/c.zig3
-rw-r--r--vendor/libghostty-vt/pkg/spirv-cross/main.zig1
-rw-r--r--vendor/libghostty-vt/pkg/utfcpp/build.zig69
-rw-r--r--vendor/libghostty-vt/pkg/utfcpp/build.zig.zon17
-rw-r--r--vendor/libghostty-vt/pkg/utfcpp/empty.cc2
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/build.zig53
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/build.zig.zon27
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/c.zig18
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/error.zig13
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig151
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/main.zig20
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/png.zig151
-rw-r--r--vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig121
-rw-r--r--vendor/libghostty-vt/pkg/zlib/build.zig71
-rw-r--r--vendor/libghostty-vt/pkg/zlib/build.zig.zon16
209 files changed, 59503 insertions, 0 deletions
diff --git a/vendor/libghostty-vt/pkg/README.md b/vendor/libghostty-vt/pkg/README.md
new file mode 100644
index 00000000..fddc4b3d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/README.md
@@ -0,0 +1,33 @@
+# Packages
+
+This folder contains packages written for and used by Ghostty that could
+potentially be useful for other projects. These are in-tree with Ghostty
+because I don't want to maintain them as separate projects (i.e. get
+dedicated issues, PRs, etc.). If you want to use them, you can copy and
+paste them into your project.
+
+## License
+
+**This license only applies to the contents of the `pkg` folder within
+the Ghostty project. This license does not apply to the rest of the
+Ghostty project.**
+
+Copyright © 2024 Mitchell Hashimoto, Ghostty contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the “Software”), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/libghostty-vt/pkg/afl++/LICENSE b/vendor/libghostty-vt/pkg/afl++/LICENSE
new file mode 100644
index 00000000..5f5fc85e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/afl++/LICENSE
@@ -0,0 +1,23 @@
+Based on zig-afl-kit: https://github.com/kristoff-it/zig-afl-kit
+
+MIT License
+
+Copyright (c) 2024 Loris Cro
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/vendor/libghostty-vt/pkg/afl++/afl.c b/vendor/libghostty-vt/pkg/afl++/afl.c
new file mode 100644
index 00000000..61eb12c4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/afl++/afl.c
@@ -0,0 +1,141 @@
+#include <limits.h>
+#include <signal.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+// AFL++ fuzzer harness for Zig fuzz targets.
+//
+// This file is the C "glue" that connects AFL++'s runtime to Zig-defined
+// fuzz test functions. We can't use AFL++'s compiler wrappers (afl-clang,
+// afl-gcc) because the code under test is compiled with Zig, so we manually
+// expand the AFL macros (__AFL_INIT, __AFL_LOOP, __AFL_FUZZ_INIT, etc.) and
+// wire up the sanitizer coverage symbols ourselves.
+
+// To ensure checks are not optimized out it is recommended to disable
+// code optimization for the fuzzer harness main()
+#pragma clang optimize off
+#pragma GCC optimize("O0")
+
+// Zig-exported entry points. zig_fuzz_init() performs one-time setup and
+// zig_fuzz_test() runs one fuzz iteration on the given input buffer.
+// The Zig object should export these.
+void zig_fuzz_init();
+void zig_fuzz_test(unsigned char*, size_t);
+
+// Linker-provided symbols marking the boundaries of the __sancov_guards
+// section. These must be declared extern so the linker provides the actual
+// section boundaries from the instrumented code, rather than creating new
+// variables that shadow them. On macOS (Mach-O), the linker uses a different
+// naming convention for section boundaries than Linux (ELF), so we use asm
+// labels to reference them.
+#ifdef __APPLE__
+extern uint32_t __start___sancov_guards __asm(
+ "section$start$__DATA$__sancov_guards");
+extern uint32_t __stop___sancov_guards __asm(
+ "section$end$__DATA$__sancov_guards");
+#else
+extern uint32_t __start___sancov_guards;
+extern uint32_t __stop___sancov_guards;
+#endif
+
+// Provided by afl-compiler-rt; initializes the guard array used by
+// SanitizerCoverage's trace-pc-guard instrumentation mode.
+void __sanitizer_cov_trace_pc_guard_init(uint32_t*, uint32_t*);
+
+// Stubs for sanitizer coverage callbacks that the Zig-compiled code references
+// but AFL's runtime (afl-compiler-rt) does not provide. Without these, linking
+// would fail with undefined symbol errors.
+__attribute__((visibility("default"))) __attribute__((
+ tls_model("initial-exec"))) _Thread_local uintptr_t __sancov_lowest_stack;
+void __sanitizer_cov_trace_pc_indir() {}
+void __sanitizer_cov_8bit_counters_init() {}
+void __sanitizer_cov_pcs_init() {}
+
+// Manual expansion of __AFL_FUZZ_INIT().
+//
+// Enables shared-memory fuzzing: AFL++ writes test cases directly into
+// shared memory (__afl_fuzz_ptr) instead of passing them via stdin, which
+// is much faster. When not running under AFL++ (e.g. standalone execution),
+// __afl_fuzz_ptr will be NULL and we fall back to reading from stdin into
+// __afl_fuzz_alt (a 1 MB static buffer).
+int __afl_sharedmem_fuzzing = 1;
+extern __attribute__((visibility("default"))) unsigned int* __afl_fuzz_len;
+extern __attribute__((visibility("default"))) unsigned char* __afl_fuzz_ptr;
+unsigned char __afl_fuzz_alt[1048576];
+unsigned char* __afl_fuzz_alt_ptr = __afl_fuzz_alt;
+
+int main(int argc, char** argv) {
+ // Tell AFL's coverage runtime about our guard section so it can track
+ // which edges in the instrumented Zig code have been hit.
+ __sanitizer_cov_trace_pc_guard_init(&__start___sancov_guards,
+ &__stop___sancov_guards);
+
+ // Manual expansion of __AFL_INIT() — deferred fork server mode.
+ //
+ // The magic string "##SIG_AFL_DEFER_FORKSRV##" is embedded in the binary
+ // so AFL++'s tooling can detect that this harness uses deferred fork
+ // server initialization. The `volatile` + `used` attributes prevent the
+ // compiler/linker from stripping it. We then call __afl_manual_init() to
+ // start the fork server at this point (after our setup) rather than at
+ // the very beginning of main().
+ static volatile const char* _A __attribute__((used, unused));
+ _A = (const char*)"##SIG_AFL_DEFER_FORKSRV##";
+#ifdef __APPLE__
+ __attribute__((visibility("default"))) void _I(void) __asm__(
+ "___afl_manual_init");
+#else
+ __attribute__((visibility("default"))) void _I(void) __asm__(
+ "__afl_manual_init");
+#endif
+ _I();
+
+ zig_fuzz_init();
+
+ // Manual expansion of __AFL_FUZZ_TESTCASE_BUF.
+ // Use shared memory buffer if available, otherwise fall back to the
+ // static buffer (for standalone/non-AFL execution).
+ unsigned char* buf = __afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr;
+
+ // Manual expansion of __AFL_LOOP(UINT_MAX) — persistent mode loop.
+ //
+ // Persistent mode keeps the process alive across many test cases instead
+ // of fork()'ing for each one, dramatically improving throughput. The magic
+ // string "##SIG_AFL_PERSISTENT##" signals to AFL++ that this binary
+ // supports persistent mode. __afl_persistent_loop() returns non-zero
+ // while there are more inputs to process.
+ //
+ // When connected to AFL++, we loop UINT_MAX times (essentially forever,
+ // AFL will restart us periodically). When running standalone, we loop
+ // once so the harness can be used for manual testing/reproduction.
+ while (({
+ static volatile const char* _B __attribute__((used, unused));
+ _B = (const char*)"##SIG_AFL_PERSISTENT##";
+ extern __attribute__((visibility("default"))) int __afl_connected;
+#ifdef __APPLE__
+ __attribute__((visibility("default"))) int _L(unsigned int) __asm__(
+ "___afl_persistent_loop");
+#else
+ __attribute__((visibility("default"))) int _L(unsigned int) __asm__(
+ "__afl_persistent_loop");
+#endif
+ _L(__afl_connected ? UINT_MAX : 1);
+ })) {
+ // Manual expansion of __AFL_FUZZ_TESTCASE_LEN.
+ // In shared-memory mode, the length is provided directly by AFL++.
+ // In standalone mode, we read from stdin into the fallback buffer.
+ int len =
+ __afl_fuzz_ptr ? *__afl_fuzz_len
+ : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff
+ ? 0
+ : *__afl_fuzz_len;
+
+ if (len >= 0) {
+ zig_fuzz_test(buf, len);
+ }
+ }
+
+ return 0;
+}
diff --git a/vendor/libghostty-vt/pkg/afl++/build.zig b/vendor/libghostty-vt/pkg/afl++/build.zig
new file mode 100644
index 00000000..9de3ad01
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/afl++/build.zig
@@ -0,0 +1,59 @@
+const std = @import("std");
+
+/// Creates a build step that produces an AFL++-instrumented fuzzing
+/// executable.
+///
+/// Returns a `LazyPath` to the resulting fuzzing executable.
+pub fn addInstrumentedExe(
+ b: *std.Build,
+ obj: *std.Build.Step.Compile,
+) std.Build.LazyPath {
+ // Force the build system to produce the binary artifact even though we
+ // only consume the LLVM bitcode below. Without this, the dependency
+ // tracking doesn't wire up correctly.
+ _ = obj.getEmittedBin();
+
+ const pkg = b.dependencyFromBuildZig(
+ @This(),
+ .{},
+ );
+
+ const afl_cc = b.addSystemCommand(&.{
+ b.findProgram(&.{"afl-cc"}, &.{}) catch
+ @panic("Could not find 'afl-cc', which is required to build"),
+ "-O3",
+ });
+ afl_cc.addArg("-o");
+ const fuzz_exe = afl_cc.addOutputFileArg(obj.name);
+ afl_cc.addFileArg(pkg.path("afl.c"));
+ afl_cc.addFileArg(obj.getEmittedLlvmBc());
+ return fuzz_exe;
+}
+
+/// Creates a run step that invokes `afl-fuzz` with the given instrumented
+/// executable, input corpus directory, and output directory.
+///
+/// Returns the `Run` step so callers can wire it into a build step.
+pub fn addFuzzerRun(
+ b: *std.Build,
+ exe: std.Build.LazyPath,
+ corpus_dir: std.Build.LazyPath,
+ output_dir: std.Build.LazyPath,
+) *std.Build.Step.Run {
+ const run = b.addSystemCommand(&.{
+ b.findProgram(&.{"afl-fuzz"}, &.{}) catch
+ @panic("Could not find 'afl-fuzz', which is required to run"),
+ "-i",
+ });
+ run.addDirectoryArg(corpus_dir);
+ run.addArgs(&.{"-o"});
+ run.addDirectoryArg(output_dir);
+ run.addArgs(&.{"--"});
+ run.addFileArg(exe);
+ return run;
+}
+
+// Required so `zig build` works although it does nothing.
+pub fn build(b: *std.Build) !void {
+ _ = b;
+}
diff --git a/vendor/libghostty-vt/pkg/afl++/build.zig.zon b/vendor/libghostty-vt/pkg/afl++/build.zig.zon
new file mode 100644
index 00000000..1fd3d5a4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/afl++/build.zig.zon
@@ -0,0 +1,11 @@
+.{
+ .name = .afl_plus_plus,
+ .fingerprint = 0x465bc4bebb188f16,
+ .version = "0.1.0",
+ .dependencies = .{},
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "afl.c",
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/android-ndk/build.zig b/vendor/libghostty-vt/pkg/android-ndk/build.zig
new file mode 100644
index 00000000..5b005665
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/android-ndk/build.zig
@@ -0,0 +1,207 @@
+const std = @import("std");
+const builtin = @import("builtin");
+
+pub fn build(_: *std.Build) !void {}
+
+// Configure the step to point to the Android NDK for libc and include
+// paths. This requires the Android NDK installed in the system and
+// setting the appropriate environment variables or installing the NDK
+// in the default location.
+//
+// The environment variables can be set as follows:
+// - `ANDROID_NDK_HOME`: Directly points to the NDK path, including the version.
+// - `ANDROID_HOME` or `ANDROID_SDK_ROOT`: Points to the Android SDK path;
+// latest available NDK will be automatically selected.
+//
+// NB: This is a workaround until zig natively supports bionic
+// cross-compilation (ziglang/zig#23906).
+pub fn addPaths(b: *std.Build, step: *std.Build.Step.Compile) !void {
+ const Cache = struct {
+ const Key = struct {
+ arch: std.Target.Cpu.Arch,
+ abi: std.Target.Abi,
+ api_level: u32,
+ };
+
+ var map: std.AutoHashMapUnmanaged(Key, ?struct {
+ libc: std.Build.LazyPath,
+ cpp_include: std.Build.LazyPath,
+ lib: std.Build.LazyPath,
+ }) = .empty;
+ };
+
+ const target = step.rootModuleTarget();
+ const gop = try Cache.map.getOrPut(b.allocator, .{
+ .arch = target.cpu.arch,
+ .abi = target.abi,
+ .api_level = target.os.version_range.linux.android,
+ });
+
+ if (!gop.found_existing) {
+ const ndk_path = findNDKPath(b) orelse return error.AndroidNDKNotFound;
+
+ const ndk_triple = ndkTriple(target) orelse {
+ gop.value_ptr.* = null;
+ return error.AndroidNDKUnsupportedTarget;
+ };
+
+ const host = hostTag() orelse {
+ gop.value_ptr.* = null;
+ return error.AndroidNDKUnsupportedHost;
+ };
+
+ const sysroot = b.pathJoin(&.{
+ ndk_path,
+ "toolchains",
+ "llvm",
+ "prebuilt",
+ host,
+ "sysroot",
+ });
+ const include_dir = b.pathJoin(&.{
+ sysroot,
+ "usr",
+ "include",
+ });
+ const sys_include_dir = b.pathJoin(&.{
+ sysroot,
+ "usr",
+ "include",
+ ndk_triple,
+ });
+ const c_runtime_dir = b.pathJoin(&.{
+ sysroot,
+ "usr",
+ "lib",
+ ndk_triple,
+ b.fmt("{d}", .{target.os.version_range.linux.android}),
+ });
+ const lib = b.pathJoin(&.{
+ sysroot,
+ "usr",
+ "lib",
+ ndk_triple,
+ });
+ const cpp_include = b.pathJoin(&.{
+ sysroot,
+ "usr",
+ "include",
+ "c++",
+ "v1",
+ });
+
+ const libc_txt = b.fmt(
+ \\include_dir={s}
+ \\sys_include_dir={s}
+ \\crt_dir={s}
+ \\msvc_lib_dir=
+ \\kernel32_lib_dir=
+ \\gcc_dir=
+ , .{ include_dir, sys_include_dir, c_runtime_dir });
+
+ const wf = b.addWriteFiles();
+ const libc_path = wf.add("libc.txt", libc_txt);
+
+ gop.value_ptr.* = .{
+ .libc = libc_path,
+ .cpp_include = .{ .cwd_relative = cpp_include },
+ .lib = .{ .cwd_relative = lib },
+ };
+ }
+
+ const value = gop.value_ptr.* orelse return error.AndroidNDKNotFound;
+
+ step.setLibCFile(value.libc);
+ step.root_module.addSystemIncludePath(value.cpp_include);
+ step.root_module.addLibraryPath(value.lib);
+}
+
+fn findNDKPath(b: *std.Build) ?[]const u8 {
+ // Check if user has set the environment variable for the NDK path.
+ if (std.process.getEnvVarOwned(b.allocator, "ANDROID_NDK_HOME") catch null) |value| {
+ if (value.len == 0) return null;
+ var dir = std.fs.openDirAbsolute(value, .{}) catch return null;
+ defer dir.close();
+ return value;
+ }
+
+ // Check the common environment variables for the Android SDK path and look for the NDK inside it.
+ inline for (.{ "ANDROID_HOME", "ANDROID_SDK_ROOT" }) |env| {
+ if (std.process.getEnvVarOwned(b.allocator, env) catch null) |sdk| {
+ if (sdk.len > 0) {
+ if (findLatestNDK(b, sdk)) |ndk| return ndk;
+ }
+ }
+ }
+
+ // As a fallback, we assume the most common/default SDK path based on the OS.
+ const home = std.process.getEnvVarOwned(
+ b.allocator,
+ if (builtin.os.tag == .windows) "LOCALAPPDATA" else "HOME",
+ ) catch return null;
+
+ const default_sdk_path = b.pathJoin(
+ &.{
+ home,
+ switch (builtin.os.tag) {
+ .linux => "Android/sdk",
+ .macos => "Library/Android/Sdk",
+ .windows => "Android/Sdk",
+ else => return null,
+ },
+ },
+ );
+
+ return findLatestNDK(b, default_sdk_path);
+}
+
+fn findLatestNDK(b: *std.Build, sdk_path: []const u8) ?[]const u8 {
+ const ndk_dir = b.pathJoin(&.{ sdk_path, "ndk" });
+ var dir = std.fs.openDirAbsolute(ndk_dir, .{ .iterate = true }) catch return null;
+ defer dir.close();
+
+ var latest_: ?struct {
+ name: []const u8,
+ version: std.SemanticVersion,
+ } = null;
+ var iterator = dir.iterate();
+
+ while (iterator.next() catch null) |file| {
+ if (file.kind != .directory) continue;
+ const version = std.SemanticVersion.parse(file.name) catch continue;
+ if (latest_) |latest| {
+ if (version.order(latest.version) != .gt) continue;
+ }
+ latest_ = .{
+ .name = file.name,
+ .version = version,
+ };
+ }
+
+ const latest = latest_ orelse return null;
+
+ return b.pathJoin(&.{ sdk_path, "ndk", latest.name });
+}
+
+fn hostTag() ?[]const u8 {
+ return switch (builtin.os.tag) {
+ .linux => "linux-x86_64",
+ // All darwin hosts use the same prebuilt binaries
+ // (https://developer.android.com/ndk/guides/other_build_systems).
+ .macos => "darwin-x86_64",
+ .windows => "windows-x86_64",
+ else => null,
+ };
+}
+
+// We must map the target architecture to the corresponding NDK triple following the NDK
+// documentation: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#architectures
+fn ndkTriple(target: std.Target) ?[]const u8 {
+ return switch (target.cpu.arch) {
+ .arm => "arm-linux-androideabi",
+ .aarch64 => "aarch64-linux-android",
+ .x86 => "i686-linux-android",
+ .x86_64 => "x86_64-linux-android",
+ else => null,
+ };
+}
diff --git a/vendor/libghostty-vt/pkg/android-ndk/build.zig.zon b/vendor/libghostty-vt/pkg/android-ndk/build.zig.zon
new file mode 100644
index 00000000..eb0de682
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/android-ndk/build.zig.zon
@@ -0,0 +1,10 @@
+.{
+ .name = .android_ndk,
+ .version = "0.0.2",
+ .fingerprint = 0xee68d62c5a97b68b,
+ .dependencies = .{},
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/apple-sdk/build.zig b/vendor/libghostty-vt/pkg/apple-sdk/build.zig
new file mode 100644
index 00000000..c573c391
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/apple-sdk/build.zig
@@ -0,0 +1,111 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+ _ = target;
+ _ = optimize;
+}
+
+/// Setup the step to point to the proper Apple SDK for libc and
+/// frameworks. This expects and relies on the native SDK being
+/// installed on the system. Ghostty doesn't support cross-compilation
+/// for Apple platforms.
+pub fn addPaths(
+ b: *std.Build,
+ step: *std.Build.Step.Compile,
+) !void {
+ // The cache. This always uses b.allocator and never frees memory
+ // (which is idiomatic for a Zig build exe). We cache the libc txt
+ // file we create because it is expensive to generate (subprocesses).
+ const Cache = struct {
+ const Key = struct {
+ arch: std.Target.Cpu.Arch,
+ os: std.Target.Os.Tag,
+ abi: std.Target.Abi,
+ };
+
+ var map: std.AutoHashMapUnmanaged(Key, ?struct {
+ libc: std.Build.LazyPath,
+ framework: []const u8,
+ system_include: []const u8,
+ library: []const u8,
+ }) = .{};
+ };
+
+ const target = step.rootModuleTarget();
+ const gop = try Cache.map.getOrPut(b.allocator, .{
+ .arch = target.cpu.arch,
+ .os = target.os.tag,
+ .abi = target.abi,
+ });
+
+ if (!gop.found_existing) {
+ // Detect our SDK using the "findNative" Zig stdlib function.
+ // This is really important because it forces using `xcrun` to
+ // find the SDK path.
+ const libc = try std.zig.LibCInstallation.findNative(.{
+ .allocator = b.allocator,
+ .target = &step.rootModuleTarget(),
+ .verbose = false,
+ });
+
+ // Render the file compatible with the `--libc` Zig flag.
+ var stream: std.io.Writer.Allocating = .init(b.allocator);
+ defer stream.deinit();
+ try libc.render(&stream.writer);
+
+ // Create a temporary file to store the libc path because
+ // `--libc` expects a file path.
+ const wf = b.addWriteFiles();
+ const path = wf.add("libc.txt", stream.written());
+
+ // Determine our framework path. Zig has a bug where it doesn't
+ // parse this from the libc txt file for `-framework` flags:
+ // https://github.com/ziglang/zig/issues/24024
+ const framework_path = framework: {
+ const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?;
+ const down2 = std.fs.path.dirname(down1).?;
+ break :framework try std.fs.path.join(b.allocator, &.{
+ down2,
+ "System",
+ "Library",
+ "Frameworks",
+ });
+ };
+
+ const library_path = library: {
+ const down1 = std.fs.path.dirname(libc.sys_include_dir.?).?;
+ break :library try std.fs.path.join(b.allocator, &.{
+ down1,
+ "lib",
+ });
+ };
+
+ gop.value_ptr.* = .{
+ .libc = path,
+ .framework = framework_path,
+ .system_include = libc.sys_include_dir.?,
+ .library = library_path,
+ };
+ }
+
+ const value = gop.value_ptr.* orelse return switch (target.os.tag) {
+ // Return a more descriptive error. Before we just returned the
+ // generic error but this was confusing a lot of community members.
+ // It costs us nothing in the build script to return something better.
+ .macos => error.XcodeMacOSSDKNotFound,
+ .ios => error.XcodeiOSSDKNotFound,
+ .tvos => error.XcodeTVOSSDKNotFound,
+ .watchos => error.XcodeWatchOSSDKNotFound,
+ else => error.XcodeAppleSDKNotFound,
+ };
+
+ step.setLibCFile(value.libc);
+
+ // This is only necessary until this bug is fixed:
+ // https://github.com/ziglang/zig/issues/24024
+ step.root_module.addSystemFrameworkPath(.{ .cwd_relative = value.framework });
+ step.root_module.addSystemIncludePath(.{ .cwd_relative = value.system_include });
+ step.root_module.addLibraryPath(.{ .cwd_relative = value.library });
+}
diff --git a/vendor/libghostty-vt/pkg/apple-sdk/build.zig.zon b/vendor/libghostty-vt/pkg/apple-sdk/build.zig.zon
new file mode 100644
index 00000000..4fa12c23
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/apple-sdk/build.zig.zon
@@ -0,0 +1,7 @@
+.{
+ .name = .apple_sdk,
+ .version = "0.1.0",
+ .dependencies = .{},
+ .fingerprint = 0xdde52860f7c464d2,
+ .paths = .{""},
+}
diff --git a/vendor/libghostty-vt/pkg/breakpad/build.zig b/vendor/libghostty-vt/pkg/breakpad/build.zig
new file mode 100644
index 00000000..56d51b15
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/breakpad/build.zig
@@ -0,0 +1,159 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addLibrary(.{
+ .name = "breakpad",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibCpp();
+ lib.addIncludePath(b.path("vendor"));
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+
+ if (b.lazyDependency("breakpad", .{})) |upstream| {
+ lib.addIncludePath(upstream.path("src"));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = common,
+ .flags = flags.items,
+ });
+
+ if (target.result.os.tag.isDarwin()) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = common_apple,
+ .flags = flags.items,
+ });
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = client_apple,
+ .flags = flags.items,
+ });
+
+ switch (target.result.os.tag) {
+ .macos => {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = common_mac,
+ .flags = flags.items,
+ });
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = client_mac,
+ .flags = flags.items,
+ });
+ },
+
+ .ios => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = client_ios,
+ .flags = flags.items,
+ }),
+
+ else => {},
+ }
+ }
+
+ if (target.result.os.tag == .linux) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = common_linux,
+ .flags = flags.items,
+ });
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = client_linux,
+ .flags = flags.items,
+ });
+ }
+
+ lib.installHeadersDirectory(
+ upstream.path("src"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+}
+
+const common: []const []const u8 = &.{
+ "src/common/convert_UTF.cc",
+ "src/common/md5.cc",
+ "src/common/string_conversion.cc",
+};
+
+const common_linux: []const []const u8 = &.{
+ "src/common/linux/elf_core_dump.cc",
+ "src/common/linux/elfutils.cc",
+ "src/common/linux/file_id.cc",
+ "src/common/linux/guid_creator.cc",
+ "src/common/linux/linux_libc_support.cc",
+ "src/common/linux/memory_mapped_file.cc",
+ "src/common/linux/safe_readlink.cc",
+ "src/common/linux/scoped_pipe.cc",
+ "src/common/linux/scoped_tmpfile.cc",
+ "src/common/linux/breakpad_getcontext.S",
+};
+
+const common_apple: []const []const u8 = &.{
+ "src/common/mac/arch_utilities.cc",
+ "src/common/mac/file_id.cc",
+ "src/common/mac/macho_id.cc",
+ "src/common/mac/macho_utilities.cc",
+ "src/common/mac/macho_walker.cc",
+ "src/common/mac/string_utilities.cc",
+};
+
+const common_mac: []const []const u8 = &.{
+ "src/common/mac/MachIPC.mm",
+ "src/common/mac/bootstrap_compat.cc",
+};
+
+const client_linux: []const []const u8 = &.{
+ "src/client/minidump_file_writer.cc",
+ "src/client/linux/crash_generation/crash_generation_client.cc",
+ "src/client/linux/crash_generation/crash_generation_server.cc",
+ "src/client/linux/dump_writer_common/thread_info.cc",
+ "src/client/linux/dump_writer_common/ucontext_reader.cc",
+ "src/client/linux/handler/exception_handler.cc",
+ "src/client/linux/handler/minidump_descriptor.cc",
+ "src/client/linux/log/log.cc",
+ "src/client/linux/microdump_writer/microdump_writer.cc",
+ "src/client/linux/minidump_writer/linux_core_dumper.cc",
+ "src/client/linux/minidump_writer/linux_dumper.cc",
+ "src/client/linux/minidump_writer/linux_ptrace_dumper.cc",
+ "src/client/linux/minidump_writer/minidump_writer.cc",
+ "src/client/linux/minidump_writer/pe_file.cc",
+};
+
+const client_apple: []const []const u8 = &.{
+ "src/client/minidump_file_writer.cc",
+ "src/client/mac/handler/breakpad_nlist_64.cc",
+ "src/client/mac/handler/dynamic_images.cc",
+ "src/client/mac/handler/minidump_generator.cc",
+};
+
+const client_mac: []const []const u8 = &.{
+ "src/client/mac/handler/exception_handler.cc",
+ "src/client/mac/crash_generation/crash_generation_client.cc",
+};
+
+const client_ios: []const []const u8 = &.{
+ "src/client/ios/exception_handler_no_mach.cc",
+ "src/client/ios/handler/ios_exception_minidump_generator.mm",
+ "src/client/mac/crash_generation/ConfigFile.mm",
+ "src/client/mac/handler/protected_memory_allocator.cc",
+};
diff --git a/vendor/libghostty-vt/pkg/breakpad/build.zig.zon b/vendor/libghostty-vt/pkg/breakpad/build.zig.zon
new file mode 100644
index 00000000..0d6c54c9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/breakpad/build.zig.zon
@@ -0,0 +1,15 @@
+.{
+ .name = .breakpad,
+ .version = "0.1.0",
+ .fingerprint = 0xfe9f9e4c76d5f962,
+ .paths = .{""},
+ .dependencies = .{
+ .breakpad = .{
+ .url = "https://deps.files.ghostty.org/breakpad-b99f444ba5f6b98cac261cbb391d8766b34a5918.tar.gz",
+ .hash = "N-V-__8AALw2uwF_03u4JRkZwRLc3Y9hakkYV7NKRR9-RIZJ",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/dcimgui/build.zig b/vendor/libghostty-vt/pkg/dcimgui/build.zig
new file mode 100644
index 00000000..01a5879d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/dcimgui/build.zig
@@ -0,0 +1,210 @@
+const std = @import("std");
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+ const freetype = b.option(bool, "freetype", "Use Freetype") orelse false;
+ const backend_opengl3 = b.option(bool, "backend-opengl3", "OpenGL3 backend") orelse false;
+ const backend_metal = b.option(bool, "backend-metal", "Metal backend") orelse false;
+ const backend_osx = b.option(bool, "backend-osx", "OSX backend") orelse false;
+
+ // Build options
+ const options = b.addOptions();
+ options.addOption(bool, "freetype", freetype);
+ options.addOption(bool, "backend_opengl3", backend_opengl3);
+ options.addOption(bool, "backend_metal", backend_metal);
+ options.addOption(bool, "backend_osx", backend_osx);
+
+ // Main static lib
+ const lib = b.addLibrary(.{
+ .name = "dcimgui",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+ b.installArtifact(lib);
+
+ // Zig module
+ const mod = b.addModule("dcimgui", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+ mod.addOptions("build_options", options);
+ mod.linkLibrary(lib);
+
+ // We need to add proper Apple SDKs to find stdlib headers
+ if (target.result.os.tag.isDarwin()) {
+ if (!target.query.isNative()) {
+ try @import("apple_sdk").addPaths(b, lib);
+ }
+ }
+
+ // Flags for C compilation, common to all.
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DIMGUI_HAS_DOCK=1",
+ "-DIMGUI_USE_WCHAR32=1",
+ "-DIMGUI_DISABLE_OBSOLETE_FUNCTIONS=1",
+ });
+ if (freetype) try flags.appendSlice(b.allocator, &.{
+ "-DIMGUI_ENABLE_FREETYPE=1",
+ });
+ if (backend_opengl3) try flags.appendSlice(b.allocator, &.{
+ "-DZIGPKG_IMGUI_ENABLE_OPENGL3=1",
+ });
+ if (target.result.os.tag == .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DIMGUI_IMPL_API=extern\t\"C\"\t__declspec(dllexport)",
+ });
+ } else {
+ try flags.appendSlice(b.allocator, &.{
+ "-DIMGUI_IMPL_API=extern\t\"C\"",
+ });
+ }
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ // Add the core Dear Imgui source files
+ if (b.lazyDependency("imgui", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "imgui_demo.cpp",
+ "imgui_draw.cpp",
+ "imgui_tables.cpp",
+ "imgui_widgets.cpp",
+ "imgui.cpp",
+ },
+ .flags = flags.items,
+ });
+
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+
+ if (freetype) {
+ lib.addCSourceFile(.{
+ .file = upstream.path("misc/freetype/imgui_freetype.cpp"),
+ .flags = flags.items,
+ });
+
+ if (b.systemIntegrationOption("freetype", .{})) {
+ lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
+ } else {
+ const freetype_dep = b.dependency("freetype", .{
+ .target = target,
+ .optimize = optimize,
+ .@"enable-libpng" = true,
+ });
+ lib.linkLibrary(freetype_dep.artifact("freetype"));
+ if (freetype_dep.builder.lazyDependency(
+ "freetype",
+ .{},
+ )) |freetype_upstream| {
+ mod.addIncludePath(freetype_upstream.path("include"));
+ }
+ }
+ }
+
+ if (backend_metal) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path("backends"),
+ .files = &.{"imgui_impl_metal.mm"},
+ .flags = flags.items,
+ });
+ lib.installHeadersDirectory(
+ upstream.path("backends"),
+ "",
+ .{ .include_extensions = &.{"imgui_impl_metal.h"} },
+ );
+ }
+ if (backend_osx) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path("backends"),
+ .files = &.{"imgui_impl_osx.mm"},
+ .flags = flags.items,
+ });
+ lib.installHeadersDirectory(
+ upstream.path("backends"),
+ "",
+ .{ .include_extensions = &.{"imgui_impl_osx.h"} },
+ );
+ }
+ if (backend_opengl3) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path("backends"),
+ .files = &.{"imgui_impl_opengl3.cpp"},
+ .flags = flags.items,
+ });
+ lib.installHeadersDirectory(
+ upstream.path("backends"),
+ "",
+ .{ .include_extensions = &.{"imgui_impl_opengl3.h"} },
+ );
+ }
+ }
+
+ // Add the C bindings
+ if (b.lazyDependency("bindings", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "dcimgui.cpp",
+ "dcimgui_internal.cpp",
+ },
+ .flags = flags.items,
+ });
+ lib.addCSourceFiles(.{
+ .root = b.path(""),
+ .files = &.{"ext.cpp"},
+ .flags = flags.items,
+ });
+
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ test_exe.root_module.addOptions("build_options", options);
+ test_exe.linkLibrary(lib);
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+}
+
+// For dynamic linking, we prefer dynamic linking and to search by
+// mode first. Mode first will search all paths for a dynamic library
+// before falling back to static.
+const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+};
diff --git a/vendor/libghostty-vt/pkg/dcimgui/build.zig.zon b/vendor/libghostty-vt/pkg/dcimgui/build.zig.zon
new file mode 100644
index 00000000..95d0120e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/dcimgui/build.zig.zon
@@ -0,0 +1,26 @@
+.{
+ .name = .dcimgui,
+ .version = "1.92.5", // -docking branch
+ .fingerprint = 0x1a25797442c6324f,
+ .paths = .{""},
+ .dependencies = .{
+ // The bindings and imgui versions below must match exactly.
+
+ .bindings = .{
+ // https://github.com/dearimgui/dear_bindings
+ .url = "https://deps.files.ghostty.org/DearBindings_v0.17_ImGui_v1.92.5-docking.tar.gz",
+ .hash = "N-V-__8AANT61wB--nJ95Gj_ctmzAtcjloZ__hRqNw5lC1Kr",
+ .lazy = true,
+ },
+
+ .imgui = .{
+ // https://github.com/ocornut/imgui
+ .url = "https://github.com/ocornut/imgui/archive/refs/tags/v1.92.5-docking.tar.gz",
+ .hash = "N-V-__8AAEbOfQBnvcFcCX2W5z7tDaN8vaNZGamEQtNOe0UI",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .freetype = .{ .path = "../freetype" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/dcimgui/ext.cpp b/vendor/libghostty-vt/pkg/dcimgui/ext.cpp
new file mode 100644
index 00000000..b686c07f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/dcimgui/ext.cpp
@@ -0,0 +1,53 @@
+#include "imgui.h"
+
+// This file contains custom extensions for functionality that isn't
+// properly supported by Dear Bindings yet. Namely:
+// https://github.com/dearimgui/dear_bindings/issues/55
+
+// Wrap this in a namespace to keep it separate from the C++ API
+namespace cimgui
+{
+#include "dcimgui.h"
+}
+
+extern "C"
+{
+CIMGUI_API void ImFontConfig_ImFontConfig(cimgui::ImFontConfig* self)
+{
+ static_assert(sizeof(cimgui::ImFontConfig) == sizeof(::ImFontConfig), "ImFontConfig size mismatch");
+ static_assert(alignof(cimgui::ImFontConfig) == alignof(::ImFontConfig), "ImFontConfig alignment mismatch");
+ ::ImFontConfig defaults;
+ *reinterpret_cast<::ImFontConfig*>(self) = defaults;
+}
+
+CIMGUI_API void ImGuiStyle_ImGuiStyle(cimgui::ImGuiStyle* self)
+{
+ static_assert(sizeof(cimgui::ImGuiStyle) == sizeof(::ImGuiStyle), "ImGuiStyle size mismatch");
+ static_assert(alignof(cimgui::ImGuiStyle) == alignof(::ImGuiStyle), "ImGuiStyle alignment mismatch");
+ ::ImGuiStyle defaults;
+ *reinterpret_cast<::ImGuiStyle*>(self) = defaults;
+}
+
+// Perform the OpenGL3 backend shutdown and then zero out the imgl3w
+// function pointer table. ImGui_ImplOpenGL3_Shutdown() calls
+// imgl3wShutdown() which dlcloses the GL library handles but does not
+// zero out the function pointers. A subsequent ImGui_ImplOpenGL3_Init()
+// sees the stale (non-null) pointers, skips loader re-initialization,
+// and crashes when calling through them. Zeroing the table forces the
+// next Init to reload the GL function pointers via imgl3wInit().
+#ifndef IMGUI_DISABLE
+#if __has_include("backends/imgui_impl_opengl3.h")
+#ifdef ZIGPKG_IMGUI_ENABLE_OPENGL3
+#include "backends/imgui_impl_opengl3.h"
+#include "backends/imgui_impl_opengl3_loader.h"
+
+CIMGUI_API void ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup()
+{
+ ::ImGui_ImplOpenGL3_Shutdown();
+ memset(&imgl3wProcs, 0, sizeof(imgl3wProcs));
+}
+#endif // ZIGPKG_IMGUI_ENABLE_OPENGL3
+#endif // __has_include("backends/imgui_impl_opengl3.h")
+#endif // IMGUI_DISABLE
+
+}
diff --git a/vendor/libghostty-vt/pkg/dcimgui/main.zig b/vendor/libghostty-vt/pkg/dcimgui/main.zig
new file mode 100644
index 00000000..40a4325c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/dcimgui/main.zig
@@ -0,0 +1,77 @@
+pub const build_options = @import("build_options");
+
+pub const c = @cImport({
+ // This is set during the build so it also has to be set
+ // during import time to get the right types. Without this
+ // you get stack size mismatches on some structs.
+ @cDefine("IMGUI_USE_WCHAR32", "1");
+
+ @cDefine("IMGUI_HAS_DOCK", "1");
+ @cInclude("dcimgui.h");
+});
+
+// OpenGL3 backend
+pub extern fn ImGui_ImplOpenGL3_Init(glsl_version: ?[*:0]const u8) callconv(.c) bool;
+pub extern fn ImGui_ImplOpenGL3_Shutdown() callconv(.c) void;
+pub extern fn ImGui_ImplOpenGL3_NewFrame() callconv(.c) void;
+pub extern fn ImGui_ImplOpenGL3_RenderDrawData(draw_data: *c.ImDrawData) callconv(.c) void;
+
+// Extension: shutdown the OpenGL3 backend and zero out the imgl3w function
+// pointer table so a subsequent Init can re-initialize the loader.
+pub extern fn ImGui_ImplOpenGL3_ShutdownWithLoaderCleanup() callconv(.c) void;
+
+// Metal backend
+pub extern fn ImGui_ImplMetal_Init(device: *anyopaque) callconv(.c) bool;
+pub extern fn ImGui_ImplMetal_Shutdown() callconv(.c) void;
+pub extern fn ImGui_ImplMetal_NewFrame(render_pass_descriptor: *anyopaque) callconv(.c) void;
+pub extern fn ImGui_ImplMetal_RenderDrawData(draw_data: *c.ImDrawData, command_buffer: *anyopaque, command_encoder: *anyopaque) callconv(.c) void;
+
+// OSX
+pub extern fn ImGui_ImplOSX_Init(*anyopaque) callconv(.c) bool;
+pub extern fn ImGui_ImplOSX_Shutdown() callconv(.c) void;
+pub extern fn ImGui_ImplOSX_NewFrame(*anyopaque) callconv(.c) void;
+
+// Internal API types and functions from dcimgui_internal.h
+// We declare these manually because the internal header contains bitfields
+// that Zig's cImport cannot translate.
+pub const ImGuiDockNodeFlagsPrivate = struct {
+ pub const DockSpace: c.ImGuiDockNodeFlags = 1 << 10;
+ pub const CentralNode: c.ImGuiDockNodeFlags = 1 << 11;
+ pub const NoTabBar: c.ImGuiDockNodeFlags = 1 << 12;
+ pub const HiddenTabBar: c.ImGuiDockNodeFlags = 1 << 13;
+ pub const NoWindowMenuButton: c.ImGuiDockNodeFlags = 1 << 14;
+ pub const NoCloseButton: c.ImGuiDockNodeFlags = 1 << 15;
+ pub const NoResizeX: c.ImGuiDockNodeFlags = 1 << 16;
+ pub const NoResizeY: c.ImGuiDockNodeFlags = 1 << 17;
+ pub const DockedWindowsInFocusRoute: c.ImGuiDockNodeFlags = 1 << 18;
+ pub const NoDockingSplitOther: c.ImGuiDockNodeFlags = 1 << 19;
+ pub const NoDockingOverMe: c.ImGuiDockNodeFlags = 1 << 20;
+ pub const NoDockingOverOther: c.ImGuiDockNodeFlags = 1 << 21;
+ pub const NoDockingOverEmpty: c.ImGuiDockNodeFlags = 1 << 22;
+};
+pub extern fn ImGui_DockBuilderDockWindow(window_name: [*:0]const u8, node_id: c.ImGuiID) callconv(.c) void;
+pub extern fn ImGui_DockBuilderGetNode(node_id: c.ImGuiID) callconv(.c) ?*anyopaque;
+pub extern fn ImGui_DockBuilderGetCentralNode(node_id: c.ImGuiID) callconv(.c) ?*anyopaque;
+pub extern fn ImGui_DockBuilderAddNode() callconv(.c) c.ImGuiID;
+pub extern fn ImGui_DockBuilderAddNodeEx(node_id: c.ImGuiID, flags: c.ImGuiDockNodeFlags) callconv(.c) c.ImGuiID;
+pub extern fn ImGui_DockBuilderRemoveNode(node_id: c.ImGuiID) callconv(.c) void;
+pub extern fn ImGui_DockBuilderRemoveNodeDockedWindows(node_id: c.ImGuiID) callconv(.c) void;
+pub extern fn ImGui_DockBuilderRemoveNodeDockedWindowsEx(node_id: c.ImGuiID, clear_settings_refs: bool) callconv(.c) void;
+pub extern fn ImGui_DockBuilderRemoveNodeChildNodes(node_id: c.ImGuiID) callconv(.c) void;
+pub extern fn ImGui_DockBuilderSetNodePos(node_id: c.ImGuiID, pos: c.ImVec2) callconv(.c) void;
+pub extern fn ImGui_DockBuilderSetNodeSize(node_id: c.ImGuiID, size: c.ImVec2) callconv(.c) void;
+pub extern fn ImGui_DockBuilderSplitNode(node_id: c.ImGuiID, split_dir: c.ImGuiDir, size_ratio_for_node_at_dir: f32, out_id_at_dir: *c.ImGuiID, out_id_at_opposite_dir: *c.ImGuiID) callconv(.c) c.ImGuiID;
+pub extern fn ImGui_DockBuilderCopyDockSpace(src_dockspace_id: c.ImGuiID, dst_dockspace_id: c.ImGuiID, in_window_remap_pairs: *c.ImVector_const_charPtr) callconv(.c) void;
+pub extern fn ImGui_DockBuilderCopyNode(src_node_id: c.ImGuiID, dst_node_id: c.ImGuiID, out_node_remap_pairs: *c.ImVector_ImGuiID) callconv(.c) void;
+pub extern fn ImGui_DockBuilderCopyWindowSettings(src_name: [*:0]const u8, dst_name: [*:0]const u8) callconv(.c) void;
+pub extern fn ImGui_DockBuilderFinish(node_id: c.ImGuiID) callconv(.c) void;
+
+// Extension functions from ext.cpp
+pub const ext = struct {
+ pub extern fn ImFontConfig_ImFontConfig(self: *c.ImFontConfig) callconv(.c) void;
+ pub extern fn ImGuiStyle_ImGuiStyle(self: *c.ImGuiStyle) callconv(.c) void;
+};
+
+test {
+ _ = c;
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/build.zig b/vendor/libghostty-vt/pkg/fontconfig/build.zig
new file mode 100644
index 00000000..7c87d1f2
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/build.zig
@@ -0,0 +1,298 @@
+const std = @import("std");
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const libxml2_enabled = b.option(bool, "enable-libxml2", "Build libxml2") orelse true;
+ const libxml2_iconv_enabled = b.option(
+ bool,
+ "enable-libxml2-iconv",
+ "Build libxml2 with iconv",
+ ) orelse (target.result.os.tag != .windows);
+ const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
+
+ const module = b.addModule("fontconfig", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+
+ if (b.systemIntegrationOption("fontconfig", .{})) {
+ module.linkSystemLibrary("fontconfig", dynamic_link_opts);
+ test_exe.linkSystemLibrary2("fontconfig", dynamic_link_opts);
+ } else {
+ const lib = try buildLib(b, module, .{
+ .target = target,
+ .optimize = optimize,
+
+ .libxml2_enabled = libxml2_enabled,
+ .libxml2_iconv_enabled = libxml2_iconv_enabled,
+ .freetype_enabled = freetype_enabled,
+
+ .dynamic_link_opts = dynamic_link_opts,
+ });
+
+ test_exe.linkLibrary(lib);
+ }
+}
+
+fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
+ const target = options.target;
+ const optimize = options.optimize;
+
+ const libxml2_enabled = options.libxml2_enabled;
+ const libxml2_iconv_enabled = options.libxml2_iconv_enabled;
+ const freetype_enabled = options.freetype_enabled;
+
+ const lib = b.addLibrary(.{
+ .name = "fontconfig",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ if (target.result.os.tag != .windows) {
+ lib.linkSystemLibrary("pthread");
+ }
+
+ lib.addIncludePath(b.path("override/include"));
+ module.addIncludePath(b.path("override/include"));
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_DIRENT_H",
+ "-DHAVE_FCNTL_H",
+ "-DHAVE_STDLIB_H",
+ "-DHAVE_STRING_H",
+ "-DHAVE_UNISTD_H",
+ "-DHAVE_SYS_PARAM_H",
+
+ "-DHAVE_MKSTEMP",
+ //"-DHAVE_GETPROGNAME",
+ //"-DHAVE_GETEXECNAME",
+ "-DHAVE_RAND",
+ //"-DHAVE_RANDOM_R",
+ "-DHAVE_VPRINTF",
+
+ "-DHAVE_FT_GET_BDF_PROPERTY",
+ "-DHAVE_FT_GET_PS_FONT_INFO",
+ "-DHAVE_FT_HAS_PS_GLYPH_NAMES",
+ "-DHAVE_FT_GET_X11_FONT_FORMAT",
+ "-DHAVE_FT_DONE_MM_VAR",
+
+ "-DHAVE_POSIX_FADVISE",
+
+ //"-DHAVE_STRUCT_STATVFS_F_BASETYPE",
+ // "-DHAVE_STRUCT_STATVFS_F_FSTYPENAME",
+ // "-DHAVE_STRUCT_STATFS_F_FLAGS",
+ // "-DHAVE_STRUCT_STATFS_F_FSTYPENAME",
+ // "-DHAVE_STRUCT_DIRENT_D_TYPE",
+
+ "-DFLEXIBLE_ARRAY_MEMBER",
+
+ "-DHAVE_STDATOMIC_PRIMITIVES",
+
+ "-DFC_GPERF_SIZE_T=size_t",
+
+ // Default errors that fontconfig can't handle
+ "-Wno-implicit-function-declaration",
+ "-Wno-int-conversion",
+
+ // https://gitlab.freedesktop.org/fontconfig/fontconfig/-/merge_requests/231
+ "-fno-sanitize=undefined",
+ "-fno-sanitize-trap=undefined",
+ });
+
+ switch (target.result.ptrBitWidth()) {
+ 32 => try flags.appendSlice(b.allocator, &.{
+ "-DSIZEOF_VOID_P=4",
+ "-DALIGNOF_VOID_P=4",
+ }),
+
+ 64 => try flags.appendSlice(b.allocator, &.{
+ "-DSIZEOF_VOID_P=8",
+ "-DALIGNOF_VOID_P=8",
+ }),
+
+ else => @panic("unsupported arch"),
+ }
+ if (target.result.os.tag == .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DFC_CACHEDIR=\"LOCAL_APPDATA_FONTCONFIG_CACHE\"",
+ "-DFC_TEMPLATEDIR=\"c:/share/fontconfig/conf.avail\"",
+ "-DCONFIGDIR=\"c:/etc/fonts/conf.d\"",
+ "-DFC_DEFAULT_FONTS=\"\\t<dir>WINDOWSFONTDIR</dir>\\n\\t<dir>WINDOWSUSERFONTDIR</dir>\\n\"",
+ });
+ } else {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_FSTATFS",
+ "-DHAVE_FSTATVFS",
+ "-DHAVE_GETOPT",
+ "-DHAVE_GETOPT_LONG",
+ "-DHAVE_LINK",
+ "-DHAVE_LRAND48",
+ "-DHAVE_LSTAT",
+ "-DHAVE_MKDTEMP",
+ "-DHAVE_MKOSTEMP",
+ "-DHAVE__MKTEMP_S",
+ "-DHAVE_MMAP",
+ "-DHAVE_PTHREAD",
+ "-DHAVE_RANDOM",
+ "-DHAVE_RAND_R",
+ "-DHAVE_READLINK",
+ "-DHAVE_SYS_MOUNT_H",
+ "-DHAVE_SYS_STATVFS_H",
+
+ "-DFC_CACHEDIR=\"/var/cache/fontconfig\"",
+ "-DFC_DEFAULT_FONTS=\"<dir>/usr/share/fonts</dir><dir>/usr/local/share/fonts</dir>\"",
+ });
+
+ if (target.result.os.tag == .freebsd) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DFC_TEMPLATEDIR=\"/usr/local/etc/fonts/conf.avail\"",
+ "-DFONTCONFIG_PATH=\"/usr/local/etc/fonts\"",
+ "-DCONFIGDIR=\"/usr/local/etc/fonts/conf.d\"",
+ });
+ } else {
+ try flags.appendSlice(b.allocator, &.{
+ "-DFC_TEMPLATEDIR=\"/usr/share/fontconfig/conf.avail\"",
+ "-DFONTCONFIG_PATH=\"/etc/fonts\"",
+ "-DCONFIGDIR=\"/usr/local/fontconfig/conf.d\"",
+ });
+ }
+
+ if (target.result.os.tag == .linux) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_SYS_STATFS_H",
+ "-DHAVE_SYS_VFS_H",
+ });
+ }
+ }
+
+ const dynamic_link_opts = options.dynamic_link_opts;
+
+ // Freetype2
+ _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
+ if (freetype_enabled) {
+ if (b.systemIntegrationOption("freetype", .{})) {
+ lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
+ } else {
+ if (b.lazyDependency(
+ "freetype",
+ .{ .target = target, .optimize = optimize },
+ )) |freetype_dep| {
+ lib.linkLibrary(freetype_dep.artifact("freetype"));
+ }
+ }
+ }
+
+ // Libxml2
+ _ = b.systemIntegrationOption("libxml2", .{}); // So it shows up in help
+ if (libxml2_enabled) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DENABLE_LIBXML2",
+ "-DLIBXML_STATIC",
+ "-DLIBXML_PUSH_ENABLED",
+ });
+ if (target.result.os.tag == .windows) {
+ // NOTE: this should be defined on all targets
+ try flags.appendSlice(b.allocator, &.{
+ "-Werror=implicit-function-declaration",
+ });
+ }
+
+ if (b.systemIntegrationOption("libxml2", .{})) {
+ lib.linkSystemLibrary2("libxml-2.0", dynamic_link_opts);
+ } else {
+ if (b.lazyDependency("libxml2", .{
+ .target = target,
+ .optimize = optimize,
+ .iconv = libxml2_iconv_enabled,
+ })) |libxml2_dep| {
+ lib.linkLibrary(libxml2_dep.artifact("xml2"));
+ }
+ }
+ }
+
+ if (b.lazyDependency("fontconfig", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ module.addIncludePath(upstream.path(""));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+
+ lib.installHeadersDirectory(
+ upstream.path("fontconfig"),
+ "fontconfig",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ return lib;
+}
+
+const headers = &.{
+ "fontconfig/fontconfig.h",
+ "fontconfig/fcprivate.h",
+ "fontconfig/fcfreetype.h",
+};
+
+const srcs: []const []const u8 = &.{
+ "src/fcatomic.c",
+ "src/fccache.c",
+ "src/fccfg.c",
+ "src/fccharset.c",
+ "src/fccompat.c",
+ "src/fcdbg.c",
+ "src/fcdefault.c",
+ "src/fcdir.c",
+ "src/fcformat.c",
+ "src/fcfreetype.c",
+ "src/fcfs.c",
+ "src/fcptrlist.c",
+ "src/fchash.c",
+ "src/fcinit.c",
+ "src/fclang.c",
+ "src/fclist.c",
+ "src/fcmatch.c",
+ "src/fcmatrix.c",
+ "src/fcname.c",
+ "src/fcobjs.c",
+ "src/fcpat.c",
+ "src/fcrange.c",
+ "src/fcserialize.c",
+ "src/fcstat.c",
+ "src/fcstr.c",
+ "src/fcweight.c",
+ "src/fcxml.c",
+ "src/ftglue.c",
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/build.zig.zon b/vendor/libghostty-vt/pkg/fontconfig/build.zig.zon
new file mode 100644
index 00000000..b1387b87
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/build.zig.zon
@@ -0,0 +1,16 @@
+.{
+ .name = .fontconfig,
+ .version = "2.14.2",
+ .fingerprint = 0x4a79a5a40c6d6d8,
+ .paths = .{""},
+ .dependencies = .{
+ .fontconfig = .{
+ .url = "https://deps.files.ghostty.org/fontconfig-2.14.2.tar.gz",
+ .hash = "N-V-__8AAIrfdwARSa-zMmxWwFuwpXf1T3asIN7s5jqi9c1v",
+ .lazy = true,
+ },
+
+ .freetype = .{ .path = "../freetype", .lazy = true },
+ .libxml2 = .{ .path = "../libxml2", .lazy = true },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/c.zig b/vendor/libghostty-vt/pkg/fontconfig/c.zig
new file mode 100644
index 00000000..6e8ae76b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("fontconfig/fontconfig.h");
+});
diff --git a/vendor/libghostty-vt/pkg/fontconfig/char_set.zig b/vendor/libghostty-vt/pkg/fontconfig/char_set.zig
new file mode 100644
index 00000000..b0e3a341
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/char_set.zig
@@ -0,0 +1,40 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const CharSet = opaque {
+ pub fn create() *CharSet {
+ return @ptrCast(c.FcCharSetCreate());
+ }
+
+ pub fn destroy(self: *CharSet) void {
+ c.FcCharSetDestroy(self.cval());
+ }
+
+ pub fn addChar(self: *CharSet, cp: u32) bool {
+ return c.FcCharSetAddChar(self.cval(), cp) == c.FcTrue;
+ }
+
+ pub fn hasChar(self: *const CharSet, cp: u32) bool {
+ return c.FcCharSetHasChar(self.cvalConst(), cp) == c.FcTrue;
+ }
+
+ pub inline fn cval(self: *CharSet) *c.struct__FcCharSet {
+ return @ptrCast(self);
+ }
+
+ pub inline fn cvalConst(self: *const CharSet) *const c.struct__FcCharSet {
+ return @ptrCast(self);
+ }
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var fs = CharSet.create();
+ defer fs.destroy();
+
+ try testing.expect(!fs.hasChar(0x20));
+ try testing.expect(fs.addChar(0x20));
+ try testing.expect(fs.hasChar(0x20));
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/common.zig b/vendor/libghostty-vt/pkg/fontconfig/common.zig
new file mode 100644
index 00000000..6ad6f568
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/common.zig
@@ -0,0 +1,139 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Error = @import("main.zig").Error;
+
+pub const Weight = enum(c_uint) {
+ thin = c.FC_WEIGHT_THIN,
+ extralight = c.FC_WEIGHT_EXTRALIGHT,
+ light = c.FC_WEIGHT_LIGHT,
+ demilight = c.FC_WEIGHT_DEMILIGHT,
+ book = c.FC_WEIGHT_BOOK,
+ regular = c.FC_WEIGHT_REGULAR,
+ medium = c.FC_WEIGHT_MEDIUM,
+ demibold = c.FC_WEIGHT_DEMIBOLD,
+ bold = c.FC_WEIGHT_BOLD,
+ extrabold = c.FC_WEIGHT_EXTRABOLD,
+ black = c.FC_WEIGHT_BLACK,
+ extrablack = c.FC_WEIGHT_EXTRABLACK,
+};
+
+pub const Slant = enum(c_uint) {
+ roman = c.FC_SLANT_ROMAN,
+ italic = c.FC_SLANT_ITALIC,
+ oblique = c.FC_SLANT_OBLIQUE,
+};
+
+pub const Spacing = enum(c_uint) {
+ proportional = c.FC_PROPORTIONAL,
+ dual = c.FC_DUAL,
+ mono = c.FC_MONO,
+ charcell = c.FC_CHARCELL,
+};
+
+pub const Property = enum {
+ family,
+ style,
+ slant,
+ weight,
+ size,
+ aspect,
+ pixel_size,
+ spacing,
+ foundry,
+ antialias,
+ hinting,
+ hint_style,
+ vertical_layout,
+ autohint,
+ global_advance,
+ width,
+ file,
+ index,
+ ft_face,
+ rasterizer,
+ outline,
+ scalable,
+ color,
+ variable,
+ scale,
+ symbol,
+ dpi,
+ rgba,
+ minspace,
+ source,
+ charset,
+ lang,
+ fontversion,
+ fullname,
+ familylang,
+ stylelang,
+ fullnamelang,
+ capability,
+ embolden,
+ embedded_bitmap,
+ decorative,
+ lcd_filter,
+ font_features,
+ font_variations,
+ namelang,
+ prgname,
+ hash,
+ postscript_name,
+ font_has_hint,
+ order,
+
+ pub fn cval(self: Property) [:0]const u8 {
+ @setEvalBranchQuota(10_000);
+ inline for (@typeInfo(Property).@"enum".fields) |field| {
+ if (self == @field(Property, field.name)) {
+ // Build our string in a comptime context so it is a binary
+ // constant and not stack allocated.
+ return comptime name: {
+ // Replace _ with ""
+ var buf: [field.name.len]u8 = undefined;
+ const count = std.mem.replace(u8, field.name, "_", "", &buf);
+ const replaced = buf[0 .. field.name.len - count];
+
+ // Build our string
+ var name: [replaced.len:0]u8 = undefined;
+ @memcpy(&name, replaced);
+ name[replaced.len] = 0;
+ const final = name;
+ break :name &final;
+ };
+ }
+ }
+
+ unreachable;
+ }
+
+ test "cval" {
+ const testing = std.testing;
+ try testing.expectEqualStrings("family", Property.family.cval());
+ try testing.expectEqualStrings("pixelsize", Property.pixel_size.cval());
+ }
+};
+
+pub const Result = enum(c_uint) {
+ match = c.FcResultMatch,
+ no_match = c.FcResultNoMatch,
+ type_mismatch = c.FcResultTypeMismatch,
+ no_id = c.FcResultNoId,
+ out_of_memory = c.FcResultOutOfMemory,
+
+ pub fn toError(self: Result) Error!void {
+ return switch (self) {
+ .match => {},
+ .no_match => Error.FontconfigNoMatch,
+ .type_mismatch => Error.FontconfigTypeMismatch,
+ .no_id => Error.FontconfigNoId,
+ .out_of_memory => Error.OutOfMemory,
+ };
+ }
+};
+
+pub const MatchKind = enum(c_uint) {
+ pattern = c.FcMatchPattern,
+ font = c.FcMatchFont,
+ scan = c.FcMatchScan,
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/config.zig b/vendor/libghostty-vt/pkg/fontconfig/config.zig
new file mode 100644
index 00000000..2b3674c5
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/config.zig
@@ -0,0 +1,61 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Error = @import("main.zig").Error;
+const CharSet = @import("char_set.zig").CharSet;
+const FontSet = @import("font_set.zig").FontSet;
+const ObjectSet = @import("object_set.zig").ObjectSet;
+const Pattern = @import("pattern.zig").Pattern;
+const Result = @import("main.zig").Result;
+const MatchKind = @import("main.zig").MatchKind;
+
+pub const Config = opaque {
+ pub fn destroy(self: *Config) void {
+ c.FcConfigDestroy(@ptrCast(self));
+ }
+
+ pub fn fontList(self: *Config, pat: *Pattern, os: *ObjectSet) *FontSet {
+ return @ptrCast(c.FcFontList(self.cval(), pat.cval(), os.cval()));
+ }
+
+ pub fn fontSort(
+ self: *Config,
+ pat: *Pattern,
+ trim: bool,
+ charset: ?[*]*CharSet,
+ ) FontSortResult {
+ var result: FontSortResult = undefined;
+ result.fs = @ptrCast(c.FcFontSort(
+ self.cval(),
+ pat.cval(),
+ if (trim) c.FcTrue else c.FcFalse,
+ @ptrCast(charset),
+ @ptrCast(&result.result),
+ ));
+
+ return result;
+ }
+
+ pub fn fontRenderPrepare(self: *Config, pat: *Pattern, font: *Pattern) Error!*Pattern {
+ return @as(
+ ?*Pattern,
+ @ptrCast(c.FcFontRenderPrepare(self.cval(), pat.cval(), font.cval())),
+ ) orelse Error.FontconfigFailed;
+ }
+
+ pub fn substituteWithPat(self: *Config, pat: *Pattern, kind: MatchKind) bool {
+ return c.FcConfigSubstitute(
+ self.cval(),
+ pat.cval(),
+ @intFromEnum(kind),
+ ) == c.FcTrue;
+ }
+
+ pub inline fn cval(self: *Config) *c.struct__FcConfig {
+ return @ptrCast(self);
+ }
+};
+
+pub const FontSortResult = struct {
+ result: Result,
+ fs: *FontSet,
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/error.zig b/vendor/libghostty-vt/pkg/fontconfig/error.zig
new file mode 100644
index 00000000..dd88b411
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/error.zig
@@ -0,0 +1,7 @@
+pub const Error = error{
+ OutOfMemory,
+ FontconfigFailed,
+ FontconfigNoMatch,
+ FontconfigTypeMismatch,
+ FontconfigNoId,
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/font_set.zig b/vendor/libghostty-vt/pkg/fontconfig/font_set.zig
new file mode 100644
index 00000000..c5a8c5f9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/font_set.zig
@@ -0,0 +1,44 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Pattern = @import("pattern.zig").Pattern;
+
+pub const FontSet = opaque {
+ pub fn create() *FontSet {
+ return @ptrCast(c.FcFontSetCreate());
+ }
+
+ pub fn destroy(self: *FontSet) void {
+ c.FcFontSetDestroy(self.cval());
+ }
+
+ pub fn fonts(self: *FontSet) []*Pattern {
+ const empty: [0]*Pattern = undefined;
+ const s = self.cval();
+ if (s.fonts == null) return &empty;
+ const ptr: [*]*Pattern = @ptrCast(@alignCast(s.fonts));
+ const len: usize = @intCast(s.nfont);
+ return ptr[0..len];
+ }
+
+ pub fn add(self: *FontSet, pat: *Pattern) bool {
+ return c.FcFontSetAdd(self.cval(), pat.cval()) == c.FcTrue;
+ }
+
+ pub fn print(self: *FontSet) void {
+ c.FcFontSetPrint(self.cval());
+ }
+
+ pub inline fn cval(self: *FontSet) *c.struct__FcFontSet {
+ return @ptrCast(@alignCast(self));
+ }
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var fs = FontSet.create();
+ defer fs.destroy();
+
+ try testing.expectEqual(@as(usize, 0), fs.fonts().len);
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/init.zig b/vendor/libghostty-vt/pkg/fontconfig/init.zig
new file mode 100644
index 00000000..6e8f4d61
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/init.zig
@@ -0,0 +1,43 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Config = @import("config.zig").Config;
+
+pub fn init() bool {
+ return c.FcInit() == c.FcTrue;
+}
+
+pub fn fini() void {
+ c.FcFini();
+}
+
+pub fn initLoadConfig() *Config {
+ return @ptrCast(c.FcInitLoadConfig());
+}
+
+pub fn initLoadConfigAndFonts() *Config {
+ return @ptrCast(c.FcInitLoadConfigAndFonts());
+}
+
+pub fn version() u32 {
+ return @intCast(c.FcGetVersion());
+}
+
+test "version" {
+ const testing = std.testing;
+ try testing.expect(version() > 0);
+}
+
+test "init" {
+ try std.testing.expect(init());
+ defer fini();
+}
+
+test "initLoadConfig" {
+ var config = initLoadConfig();
+ defer config.destroy();
+}
+
+test "initLoadConfigAndFonts" {
+ var config = initLoadConfigAndFonts();
+ defer config.destroy();
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/lang_set.zig b/vendor/libghostty-vt/pkg/fontconfig/lang_set.zig
new file mode 100644
index 00000000..abefcc3e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/lang_set.zig
@@ -0,0 +1,61 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const LangSet = opaque {
+ pub fn create() *LangSet {
+ return @ptrCast(c.FcLangSetCreate());
+ }
+
+ pub fn destroy(self: *LangSet) void {
+ c.FcLangSetDestroy(self.cval());
+ }
+
+ pub fn addLang(self: *LangSet, lang: [:0]const u8) bool {
+ return c.FcLangSetAdd(self.cval(), lang.ptr) == c.FcTrue;
+ }
+
+ pub fn hasLang(self: *const LangSet, lang: [:0]const u8) bool {
+ return c.FcLangSetHasLang(self.cvalConst(), lang.ptr) == c.FcLangEqual;
+ }
+
+ pub inline fn cval(self: *LangSet) *c.struct__FcLangSet {
+ return @ptrCast(self);
+ }
+
+ pub inline fn cvalConst(self: *const LangSet) *const c.struct__FcLangSet {
+ return @ptrCast(self);
+ }
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var fs = LangSet.create();
+ defer fs.destroy();
+
+ try testing.expect(!fs.hasLang("und-zsye"));
+}
+
+test "hasLang exact match" {
+ const testing = std.testing;
+
+ // Test exact match: langset with "en-US" should return true for "en-US"
+ var fs = LangSet.create();
+ defer fs.destroy();
+ try testing.expect(fs.addLang("en-US"));
+ try testing.expect(fs.hasLang("en-US"));
+
+ // Test exact match: langset with "und-zsye" should return true for "und-zsye"
+ var fs_emoji = LangSet.create();
+ defer fs_emoji.destroy();
+ try testing.expect(fs_emoji.addLang("und-zsye"));
+ try testing.expect(fs_emoji.hasLang("und-zsye"));
+
+ // Test mismatch: langset with "en-US" should return false for "fr"
+ try testing.expect(!fs.hasLang("fr"));
+
+ // Test partial match: langset with "en-US" should return false for "en-GB"
+ // (different territory, but we only want exact matches)
+ try testing.expect(!fs.hasLang("en-GB"));
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/main.zig b/vendor/libghostty-vt/pkg/fontconfig/main.zig
new file mode 100644
index 00000000..61f8a329
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/main.zig
@@ -0,0 +1,45 @@
+const initpkg = @import("init.zig");
+const char_set = @import("char_set.zig");
+const common = @import("common.zig");
+const config = @import("config.zig");
+const errorpkg = @import("error.zig");
+const font_set = @import("font_set.zig");
+const lang_set = @import("lang_set.zig");
+const matrix = @import("matrix.zig");
+const object_set = @import("object_set.zig");
+const pattern = @import("pattern.zig");
+const range = @import("range.zig");
+const value = @import("value.zig");
+
+pub const c = @import("c.zig").c;
+pub const init = initpkg.init;
+pub const fini = initpkg.fini;
+pub const initLoadConfig = initpkg.initLoadConfig;
+pub const initLoadConfigAndFonts = initpkg.initLoadConfigAndFonts;
+pub const version = initpkg.version;
+pub const CharSet = char_set.CharSet;
+pub const Weight = common.Weight;
+pub const Slant = common.Slant;
+pub const Spacing = common.Spacing;
+pub const Property = common.Property;
+pub const Result = common.Result;
+pub const MatchKind = common.MatchKind;
+pub const Config = config.Config;
+pub const Error = errorpkg.Error;
+pub const FontSet = font_set.FontSet;
+pub const LangSet = lang_set.LangSet;
+pub const Matrix = matrix.Matrix;
+pub const ObjectSet = object_set.ObjectSet;
+pub const Pattern = pattern.Pattern;
+pub const Range = range.Range;
+pub const Type = value.Type;
+pub const Value = value.Value;
+pub const ValueBinding = value.ValueBinding;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
+
+test {
+ _ = @import("test.zig");
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/matrix.zig b/vendor/libghostty-vt/pkg/fontconfig/matrix.zig
new file mode 100644
index 00000000..a9b05a9e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/matrix.zig
@@ -0,0 +1,10 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const Matrix = extern struct {
+ xx: f64,
+ xy: f64,
+ yx: f64,
+ yy: f64,
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/object_set.zig b/vendor/libghostty-vt/pkg/fontconfig/object_set.zig
new file mode 100644
index 00000000..29cf6386
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/object_set.zig
@@ -0,0 +1,30 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Property = @import("main.zig").Property;
+
+pub const ObjectSet = opaque {
+ pub fn create() *ObjectSet {
+ return @ptrCast(c.FcObjectSetCreate());
+ }
+
+ pub fn destroy(self: *ObjectSet) void {
+ c.FcObjectSetDestroy(self.cval());
+ }
+
+ pub fn add(self: *ObjectSet, p: Property) bool {
+ return c.FcObjectSetAdd(self.cval(), p.cval().ptr) == c.FcTrue;
+ }
+
+ pub inline fn cval(self: *ObjectSet) *c.struct__FcObjectSet {
+ return @ptrCast(@alignCast(self));
+ }
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var os = ObjectSet.create();
+ defer os.destroy();
+
+ try testing.expect(os.add(.family));
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/override/include/fcalias.h b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcalias.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcalias.h
diff --git a/vendor/libghostty-vt/pkg/fontconfig/override/include/fcaliastail.h b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcaliastail.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcaliastail.h
diff --git a/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftalias.h b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftalias.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftalias.h
diff --git a/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftaliastail.h b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftaliastail.h
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcftaliastail.h
diff --git a/vendor/libghostty-vt/pkg/fontconfig/override/include/fcobjshash.h b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcobjshash.h
new file mode 100644
index 00000000..f06234ac
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/override/include/fcobjshash.h
@@ -0,0 +1,347 @@
+/* ANSI-C code produced by gperf version 3.1 */
+/* Command-line: gperf --pic -m 100 fcobjshash.gperf */
+/* Computed positions: -k'3,5' */
+
+#if !((' ' == 32) && ('!' == 33) && ('"' == 34) && ('#' == 35) \
+ && ('%' == 37) && ('&' == 38) && ('\'' == 39) && ('(' == 40) \
+ && (')' == 41) && ('*' == 42) && ('+' == 43) && (',' == 44) \
+ && ('-' == 45) && ('.' == 46) && ('/' == 47) && ('0' == 48) \
+ && ('1' == 49) && ('2' == 50) && ('3' == 51) && ('4' == 52) \
+ && ('5' == 53) && ('6' == 54) && ('7' == 55) && ('8' == 56) \
+ && ('9' == 57) && (':' == 58) && (';' == 59) && ('<' == 60) \
+ && ('=' == 61) && ('>' == 62) && ('?' == 63) && ('A' == 65) \
+ && ('B' == 66) && ('C' == 67) && ('D' == 68) && ('E' == 69) \
+ && ('F' == 70) && ('G' == 71) && ('H' == 72) && ('I' == 73) \
+ && ('J' == 74) && ('K' == 75) && ('L' == 76) && ('M' == 77) \
+ && ('N' == 78) && ('O' == 79) && ('P' == 80) && ('Q' == 81) \
+ && ('R' == 82) && ('S' == 83) && ('T' == 84) && ('U' == 85) \
+ && ('V' == 86) && ('W' == 87) && ('X' == 88) && ('Y' == 89) \
+ && ('Z' == 90) && ('[' == 91) && ('\\' == 92) && (']' == 93) \
+ && ('^' == 94) && ('_' == 95) && ('a' == 97) && ('b' == 98) \
+ && ('c' == 99) && ('d' == 100) && ('e' == 101) && ('f' == 102) \
+ && ('g' == 103) && ('h' == 104) && ('i' == 105) && ('j' == 106) \
+ && ('k' == 107) && ('l' == 108) && ('m' == 109) && ('n' == 110) \
+ && ('o' == 111) && ('p' == 112) && ('q' == 113) && ('r' == 114) \
+ && ('s' == 115) && ('t' == 116) && ('u' == 117) && ('v' == 118) \
+ && ('w' == 119) && ('x' == 120) && ('y' == 121) && ('z' == 122) \
+ && ('{' == 123) && ('|' == 124) && ('}' == 125) && ('~' == 126))
+/* The character set is not based on ISO-646. */
+#error "gperf generated tables don't work with this execution character set. Please report a bug to <bug-gperf@gnu.org>."
+#endif
+
+#line 1 "fcobjshash.gperf"
+
+#line 13 "fcobjshash.gperf"
+struct FcObjectTypeInfo {
+int name;
+int id;
+};
+#include <string.h>
+/* maximum key range = 59, duplicates = 0 */
+
+#ifdef __GNUC__
+__inline
+#else
+#ifdef __cplusplus
+inline
+#endif
+#endif
+static unsigned int
+FcObjectTypeHash (register const char *str, register size_t len)
+{
+ static const unsigned char asso_values[] =
+ {
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 4, 10, 32,
+ 21, 29, 28, 49, 14, 4, 66, 66, 5, 31,
+ 18, 22, 27, 66, 15, 9, 8, 23, 23, 13,
+ 23, 16, 4, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66, 66, 66, 66, 66,
+ 66, 66, 66, 66, 66, 66
+ };
+ register unsigned int hval = len;
+
+ switch (hval)
+ {
+ default:
+ hval += asso_values[(unsigned char)str[4]];
+ /*FALLTHROUGH*/
+ case 4:
+ case 3:
+ hval += asso_values[(unsigned char)str[2]];
+ break;
+ }
+ return hval;
+}
+
+struct FcObjectTypeNamePool_t
+ {
+ char FcObjectTypeNamePool_str7[sizeof("dpi")];
+ char FcObjectTypeNamePool_str8[sizeof("size")];
+ char FcObjectTypeNamePool_str9[sizeof("file")];
+ char FcObjectTypeNamePool_str13[sizeof("hash")];
+ char FcObjectTypeNamePool_str14[sizeof("rgba")];
+ char FcObjectTypeNamePool_str15[sizeof("spacing")];
+ char FcObjectTypeNamePool_str16[sizeof("scalable")];
+ char FcObjectTypeNamePool_str17[sizeof("slant")];
+ char FcObjectTypeNamePool_str18[sizeof("matrix")];
+ char FcObjectTypeNamePool_str19[sizeof("outline")];
+ char FcObjectTypeNamePool_str20[sizeof("charset")];
+ char FcObjectTypeNamePool_str21[sizeof("antialias")];
+ char FcObjectTypeNamePool_str22[sizeof("lang")];
+ char FcObjectTypeNamePool_str23[sizeof("embolden")];
+ char FcObjectTypeNamePool_str24[sizeof("weight")];
+ char FcObjectTypeNamePool_str25[sizeof("color")];
+ char FcObjectTypeNamePool_str26[sizeof("charwidth")];
+ char FcObjectTypeNamePool_str27[sizeof("variable")];
+ char FcObjectTypeNamePool_str28[sizeof("charheight")];
+ char FcObjectTypeNamePool_str29[sizeof("hinting")];
+ char FcObjectTypeNamePool_str30[sizeof("autohint")];
+ char FcObjectTypeNamePool_str31[sizeof("fullname")];
+ char FcObjectTypeNamePool_str32[sizeof("postscriptname")];
+ char FcObjectTypeNamePool_str33[sizeof("verticallayout")];
+ char FcObjectTypeNamePool_str34[sizeof("lcdfilter")];
+ char FcObjectTypeNamePool_str35[sizeof("fullnamelang")];
+ char FcObjectTypeNamePool_str36[sizeof("hintstyle")];
+ char FcObjectTypeNamePool_str37[sizeof("pixelsize")];
+ char FcObjectTypeNamePool_str38[sizeof("scale")];
+ char FcObjectTypeNamePool_str39[sizeof("globaladvance")];
+ char FcObjectTypeNamePool_str40[sizeof("width")];
+ char FcObjectTypeNamePool_str41[sizeof("order")];
+ char FcObjectTypeNamePool_str42[sizeof("family")];
+ char FcObjectTypeNamePool_str43[sizeof("fonthashint")];
+ char FcObjectTypeNamePool_str44[sizeof("namelang")];
+ char FcObjectTypeNamePool_str45[sizeof("embeddedbitmap")];
+ char FcObjectTypeNamePool_str46[sizeof("familylang")];
+ char FcObjectTypeNamePool_str47[sizeof("capability")];
+ char FcObjectTypeNamePool_str48[sizeof("rasterizer")];
+ char FcObjectTypeNamePool_str49[sizeof("index")];
+ char FcObjectTypeNamePool_str50[sizeof("style")];
+ char FcObjectTypeNamePool_str51[sizeof("foundry")];
+ char FcObjectTypeNamePool_str52[sizeof("fontversion")];
+ char FcObjectTypeNamePool_str53[sizeof("minspace")];
+ char FcObjectTypeNamePool_str54[sizeof("stylelang")];
+ char FcObjectTypeNamePool_str55[sizeof("fontvariations")];
+ char FcObjectTypeNamePool_str56[sizeof("fontformat")];
+ char FcObjectTypeNamePool_str57[sizeof("decorative")];
+ char FcObjectTypeNamePool_str58[sizeof("fontfeatures")];
+ char FcObjectTypeNamePool_str59[sizeof("symbol")];
+ char FcObjectTypeNamePool_str60[sizeof("prgname")];
+ char FcObjectTypeNamePool_str65[sizeof("aspect")];
+ };
+static const struct FcObjectTypeNamePool_t FcObjectTypeNamePool_contents =
+ {
+ "dpi",
+ "size",
+ "file",
+ "hash",
+ "rgba",
+ "spacing",
+ "scalable",
+ "slant",
+ "matrix",
+ "outline",
+ "charset",
+ "antialias",
+ "lang",
+ "embolden",
+ "weight",
+ "color",
+ "charwidth",
+ "variable",
+ "charheight",
+ "hinting",
+ "autohint",
+ "fullname",
+ "postscriptname",
+ "verticallayout",
+ "lcdfilter",
+ "fullnamelang",
+ "hintstyle",
+ "pixelsize",
+ "scale",
+ "globaladvance",
+ "width",
+ "order",
+ "family",
+ "fonthashint",
+ "namelang",
+ "embeddedbitmap",
+ "familylang",
+ "capability",
+ "rasterizer",
+ "index",
+ "style",
+ "foundry",
+ "fontversion",
+ "minspace",
+ "stylelang",
+ "fontvariations",
+ "fontformat",
+ "decorative",
+ "fontfeatures",
+ "symbol",
+ "prgname",
+ "aspect"
+ };
+#define FcObjectTypeNamePool ((const char *) &FcObjectTypeNamePool_contents)
+const struct FcObjectTypeInfo *
+FcObjectTypeLookup (register const char *str, register size_t len)
+{
+ enum
+ {
+ TOTAL_KEYWORDS = 52,
+ MIN_WORD_LENGTH = 3,
+ MAX_WORD_LENGTH = 14,
+ MIN_HASH_VALUE = 7,
+ MAX_HASH_VALUE = 65
+ };
+
+ static const struct FcObjectTypeInfo wordlist[] =
+ {
+ {-1}, {-1}, {-1}, {-1}, {-1}, {-1}, {-1},
+#line 43 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str7,FC_DPI_OBJECT},
+#line 27 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str8,FC_SIZE_OBJECT},
+#line 38 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str9,FC_FILE_OBJECT},
+ {-1}, {-1}, {-1},
+#line 62 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str13,FC_HASH_OBJECT},
+#line 44 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str14,FC_RGBA_OBJECT},
+#line 30 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str15,FC_SPACING_OBJECT},
+#line 42 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str16,FC_SCALABLE_OBJECT},
+#line 24 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str17,FC_SLANT_OBJECT},
+#line 49 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str18,FC_MATRIX_OBJECT},
+#line 41 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str19,FC_OUTLINE_OBJECT},
+#line 50 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str20,FC_CHARSET_OBJECT},
+#line 32 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str21,FC_ANTIALIAS_OBJECT},
+#line 51 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str22,FC_LANG_OBJECT},
+#line 55 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str23,FC_EMBOLDEN_OBJECT},
+#line 25 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str24,FC_WEIGHT_OBJECT},
+#line 64 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str25,FC_COLOR_OBJECT},
+#line 47 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str26,FC_CHARWIDTH_OBJECT},
+#line 67 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str27,FC_VARIABLE_OBJECT},
+#line 48 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str28,FC_CHAR_HEIGHT_OBJECT},
+#line 34 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str29,FC_HINTING_OBJECT},
+#line 36 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str30,FC_AUTOHINT_OBJECT},
+#line 22 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str31,FC_FULLNAME_OBJECT},
+#line 63 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str32,FC_POSTSCRIPT_NAME_OBJECT},
+#line 35 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str33,FC_VERTICAL_LAYOUT_OBJECT},
+#line 58 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str34,FC_LCD_FILTER_OBJECT},
+#line 23 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str35,FC_FULLNAMELANG_OBJECT},
+#line 33 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str36,FC_HINT_STYLE_OBJECT},
+#line 29 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str37,FC_PIXEL_SIZE_OBJECT},
+#line 45 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str38,FC_SCALE_OBJECT},
+#line 37 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str39,FC_GLOBAL_ADVANCE_OBJECT},
+#line 26 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str40,FC_WIDTH_OBJECT},
+#line 69 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str41,FC_ORDER_OBJECT},
+#line 18 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str42,FC_FAMILY_OBJECT},
+#line 68 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str43,FC_FONT_HAS_HINT_OBJECT},
+#line 59 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str44,FC_NAMELANG_OBJECT},
+#line 56 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str45,FC_EMBEDDED_BITMAP_OBJECT},
+#line 19 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str46,FC_FAMILYLANG_OBJECT},
+#line 53 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str47,FC_CAPABILITY_OBJECT},
+#line 40 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str48,FC_RASTERIZER_OBJECT},
+#line 39 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str49,FC_INDEX_OBJECT},
+#line 20 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str50,FC_STYLE_OBJECT},
+#line 31 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str51,FC_FOUNDRY_OBJECT},
+#line 52 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str52,FC_FONTVERSION_OBJECT},
+#line 46 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str53,FC_MINSPACE_OBJECT},
+#line 21 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str54,FC_STYLELANG_OBJECT},
+#line 66 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str55,FC_FONT_VARIATIONS_OBJECT},
+#line 54 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str56,FC_FONTFORMAT_OBJECT},
+#line 57 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str57,FC_DECORATIVE_OBJECT},
+#line 60 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str58,FC_FONT_FEATURES_OBJECT},
+#line 65 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str59,FC_SYMBOL_OBJECT},
+#line 61 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str60,FC_PRGNAME_OBJECT},
+ {-1}, {-1}, {-1}, {-1},
+#line 28 "fcobjshash.gperf"
+ {(int)(size_t)&((struct FcObjectTypeNamePool_t *)0)->FcObjectTypeNamePool_str65,FC_ASPECT_OBJECT}
+ };
+
+ if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
+ {
+ register unsigned int key = FcObjectTypeHash (str, len);
+
+ if (key <= MAX_HASH_VALUE)
+ {
+ register int o = wordlist[key].name;
+ if (o >= 0)
+ {
+ register const char *s = o + FcObjectTypeNamePool;
+
+ if (*str == *s && !strcmp (str + 1, s + 1))
+ return &wordlist[key];
+ }
+ }
+ }
+ return 0;
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/pattern.zig b/vendor/libghostty-vt/pkg/fontconfig/pattern.zig
new file mode 100644
index 00000000..3a623e22
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/pattern.zig
@@ -0,0 +1,168 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Error = @import("main.zig").Error;
+const ObjectSet = @import("main.zig").ObjectSet;
+const Property = @import("main.zig").Property;
+const Result = @import("main.zig").Result;
+const Value = @import("main.zig").Value;
+const ValueBinding = @import("main.zig").ValueBinding;
+const Weight = @import("main.zig").Weight;
+
+pub const Pattern = opaque {
+ pub fn create() *Pattern {
+ return @ptrCast(c.FcPatternCreate());
+ }
+
+ pub fn parse(str: [:0]const u8) *Pattern {
+ return @ptrCast(c.FcNameParse(str.ptr));
+ }
+
+ pub fn destroy(self: *Pattern) void {
+ c.FcPatternDestroy(self.cval());
+ }
+
+ pub fn defaultSubstitute(self: *Pattern) void {
+ c.FcDefaultSubstitute(self.cval());
+ }
+
+ pub fn add(self: *Pattern, prop: Property, value: Value, append: bool) bool {
+ return c.FcPatternAdd(
+ self.cval(),
+ prop.cval().ptr,
+ value.cval(),
+ if (append) c.FcTrue else c.FcFalse,
+ ) == c.FcTrue;
+ }
+
+ pub fn get(self: *Pattern, prop: Property, id: u32) Error!Value {
+ var val: c.struct__FcValue = undefined;
+ try @as(Result, @enumFromInt(c.FcPatternGet(
+ self.cval(),
+ prop.cval().ptr,
+ @intCast(id),
+ &val,
+ ))).toError();
+
+ return .init(&val);
+ }
+
+ pub fn delete(self: *Pattern, prop: Property) bool {
+ return c.FcPatternDel(self.cval(), prop.cval()) == c.FcTrue;
+ }
+
+ pub fn filter(self: *Pattern, os: *const ObjectSet) *Pattern {
+ return @ptrCast(c.FcPatternFilter(self.cval(), os.cval()));
+ }
+
+ pub fn objectIterator(self: *Pattern) ObjectIterator {
+ return .{ .pat = self.cval(), .iter = null };
+ }
+
+ pub fn print(self: *Pattern) void {
+ c.FcPatternPrint(self.cval());
+ }
+
+ pub inline fn cval(self: *Pattern) *c.struct__FcPattern {
+ return @ptrCast(self);
+ }
+
+ pub const ObjectIterator = struct {
+ pat: *c.struct__FcPattern,
+ iter: ?c.struct__FcPatternIter,
+
+ /// Move to the next object, returns true if there is another
+ /// object and false otherwise. If this is the first call, this
+ /// will be the first object.
+ pub fn next(self: *ObjectIterator) bool {
+ // Null means our first iterator
+ if (self.iter == null) {
+ // If we have no objects, do not create iterator
+ if (c.FcPatternObjectCount(self.pat) == 0) return false;
+
+ var iter: c.struct__FcPatternIter = undefined;
+ c.FcPatternIterStart(
+ self.pat,
+ &iter,
+ );
+ assert(c.FcPatternIterIsValid(self.pat, &iter) == c.FcTrue);
+ self.iter = iter;
+
+ // Return right away because the fontconfig iterator pattern
+ // is do/while.
+ return true;
+ }
+
+ return c.FcPatternIterNext(self.pat, @ptrCast(&self.iter)) == c.FcTrue;
+ }
+
+ pub fn object(self: *ObjectIterator) []const u8 {
+ return std.mem.sliceTo(c.FcPatternIterGetObject(
+ self.pat,
+ &self.iter.?,
+ ), 0);
+ }
+
+ pub fn valueLen(self: *ObjectIterator) usize {
+ return @intCast(c.FcPatternIterValueCount(self.pat, &self.iter.?));
+ }
+
+ pub fn valueIterator(self: *ObjectIterator) ValueIterator {
+ return .{
+ .pat = self.pat,
+ .iter = &self.iter.?,
+ .max = c.FcPatternIterValueCount(self.pat, &self.iter.?),
+ };
+ }
+ };
+
+ pub const ValueIterator = struct {
+ pat: *c.struct__FcPattern,
+ iter: *c.struct__FcPatternIter,
+ max: c_int,
+ id: c_int = 0,
+
+ pub const Entry = struct {
+ result: Result,
+ value: Value,
+ binding: ValueBinding,
+ };
+
+ pub fn next(self: *ValueIterator) ?Entry {
+ if (self.id >= self.max) return null;
+ var value: c.struct__FcValue = undefined;
+ var binding: c.FcValueBinding = undefined;
+ const result = c.FcPatternIterGetValue(self.pat, self.iter, self.id, &value, &binding);
+ self.id += 1;
+
+ return Entry{
+ .result = @enumFromInt(result),
+ .binding = @enumFromInt(binding),
+ .value = .init(&value),
+ };
+ }
+ };
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var pat = Pattern.create();
+ defer pat.destroy();
+
+ try testing.expect(pat.add(.family, .{ .string = "monospace" }, false));
+ try testing.expect(pat.add(.weight, .{ .integer = @intFromEnum(Weight.bold) }, false));
+
+ {
+ const val = try pat.get(.family, 0);
+ try testing.expect(val == .string);
+ try testing.expectEqualStrings("monospace", val.string);
+ }
+}
+
+test "name parse" {
+ var pat = Pattern.parse(":monospace");
+ defer pat.destroy();
+
+ pat.defaultSubstitute();
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/range.zig b/vendor/libghostty-vt/pkg/fontconfig/range.zig
new file mode 100644
index 00000000..9eb0ef26
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/range.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const Range = opaque {
+ pub fn destroy(self: *Range) void {
+ c.FcRangeDestroy(self.cval());
+ }
+
+ pub inline fn cval(self: *Range) *c.struct__FcRange {
+ return @ptrCast(self);
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/fontconfig/test.zig b/vendor/libghostty-vt/pkg/fontconfig/test.zig
new file mode 100644
index 00000000..bd121c1b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/test.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const fontconfig = @import("main.zig");
+
+test "fc-list" {
+ const testing = std.testing;
+
+ var cfg = fontconfig.initLoadConfigAndFonts();
+ defer cfg.destroy();
+
+ var pat = fontconfig.Pattern.create();
+ defer pat.destroy();
+
+ var os = fontconfig.ObjectSet.create();
+ defer os.destroy();
+
+ var fs = cfg.fontList(pat, os);
+ defer fs.destroy();
+
+ // Note: this is environmental, but in general we expect all our
+ // testing environments to have at least one font.
+ try testing.expect(fs.fonts().len > 0);
+}
+
+test "fc-match" {
+ const testing = std.testing;
+
+ var cfg = fontconfig.initLoadConfigAndFonts();
+ defer cfg.destroy();
+
+ var pat = fontconfig.Pattern.create();
+ errdefer pat.destroy();
+ try testing.expect(cfg.substituteWithPat(pat, .pattern));
+ pat.defaultSubstitute();
+
+ const result = cfg.fontSort(pat, false, null);
+ errdefer result.fs.destroy();
+
+ var fs = fontconfig.FontSet.create();
+ defer fs.destroy();
+ defer for (fs.fonts()) |font| font.destroy();
+
+ {
+ const fonts = result.fs.fonts();
+ try testing.expect(fonts.len > 0);
+ for (fonts) |font| {
+ const pat_prep = try cfg.fontRenderPrepare(pat, font);
+ try testing.expect(fs.add(pat_prep));
+ }
+ result.fs.destroy();
+ pat.destroy();
+ }
+
+ {
+ for (fs.fonts()) |font| {
+ var it = font.objectIterator();
+ while (it.next()) {
+ try testing.expect(it.object().len > 0);
+ try testing.expect(it.valueLen() > 0);
+ var value_it = it.valueIterator();
+ while (value_it.next()) |entry| {
+ try testing.expect(entry.value != .unknown);
+ }
+ }
+ }
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/fontconfig/value.zig b/vendor/libghostty-vt/pkg/fontconfig/value.zig
new file mode 100644
index 00000000..89f796a2
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/fontconfig/value.zig
@@ -0,0 +1,76 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const CharSet = @import("main.zig").CharSet;
+const LangSet = @import("main.zig").LangSet;
+const Matrix = @import("main.zig").Matrix;
+const Range = @import("main.zig").Range;
+
+pub const Type = enum(c_int) {
+ unknown = c.FcTypeUnknown,
+ void = c.FcTypeVoid,
+ integer = c.FcTypeInteger,
+ double = c.FcTypeDouble,
+ string = c.FcTypeString,
+ bool = c.FcTypeBool,
+ matrix = c.FcTypeMatrix,
+ char_set = c.FcTypeCharSet,
+ ft_face = c.FcTypeFTFace,
+ lang_set = c.FcTypeLangSet,
+ range = c.FcTypeRange,
+};
+
+pub const Value = union(Type) {
+ unknown: void,
+ void: void,
+ integer: i32,
+ double: f64,
+ string: [:0]const u8,
+ bool: bool,
+ matrix: *const Matrix,
+ char_set: *const CharSet,
+ ft_face: *anyopaque,
+ lang_set: *const LangSet,
+ range: *const Range,
+
+ pub fn init(cvalue: *c.struct__FcValue) Value {
+ return switch (@as(Type, @enumFromInt(cvalue.type))) {
+ .unknown => .{ .unknown = {} },
+ .void => .{ .void = {} },
+ .string => .{ .string = std.mem.sliceTo(cvalue.u.s, 0) },
+ .integer => .{ .integer = @intCast(cvalue.u.i) },
+ .double => .{ .double = cvalue.u.d },
+ .bool => .{ .bool = cvalue.u.b == c.FcTrue },
+ .matrix => .{ .matrix = @ptrCast(cvalue.u.m) },
+ .char_set => .{ .char_set = @ptrCast(cvalue.u.c) },
+ .ft_face => .{ .ft_face = @ptrCast(cvalue.u.f) },
+ .lang_set => .{ .lang_set = @ptrCast(cvalue.u.l) },
+ .range => .{ .range = @ptrCast(cvalue.u.r) },
+ };
+ }
+
+ pub fn cval(self: Value) c.struct__FcValue {
+ return .{
+ .type = @intFromEnum(std.meta.activeTag(self)),
+ .u = switch (self) {
+ .unknown => undefined,
+ .void => undefined,
+ .integer => |v| .{ .i = @intCast(v) },
+ .double => |v| .{ .d = v },
+ .string => |v| .{ .s = v.ptr },
+ .bool => |v| .{ .b = if (v) c.FcTrue else c.FcFalse },
+ .matrix => |v| .{ .m = @ptrCast(v) },
+ .char_set => |v| .{ .c = @ptrCast(v) },
+ .ft_face => |v| .{ .f = v },
+ .lang_set => |v| .{ .l = @ptrCast(v) },
+ .range => |v| .{ .r = @ptrCast(v) },
+ },
+ };
+ }
+};
+
+pub const ValueBinding = enum(c_int) {
+ weak = c.FcValueBindingWeak,
+ strong = c.FcValueBindingStrong,
+ same = c.FcValueBindingSame,
+};
diff --git a/vendor/libghostty-vt/pkg/freetype/Library.zig b/vendor/libghostty-vt/pkg/freetype/Library.zig
new file mode 100644
index 00000000..1cff7921
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/Library.zig
@@ -0,0 +1,92 @@
+const Library = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const Face = @import("face.zig").Face;
+const errors = @import("errors.zig");
+const Error = errors.Error;
+const intToError = errors.intToError;
+
+handle: c.FT_Library,
+
+/// Initialize a new FreeType library object. The set of modules that are
+/// registered by this function is determined at build time.
+pub fn init() Error!Library {
+ var res = Library{ .handle = undefined };
+ try intToError(c.FT_Init_FreeType(&res.handle));
+ return res;
+}
+
+/// Destroy a given FreeType library object and all of its children,
+/// including resources, drivers, faces, sizes, etc.
+pub fn deinit(self: Library) void {
+ _ = c.FT_Done_FreeType(self.handle);
+}
+
+/// Return the version of the FreeType library being used. This is useful when
+/// dynamically linking to the library, since one cannot use the macros
+/// FREETYPE_MAJOR, FREETYPE_MINOR, and FREETYPE_PATCH.
+pub fn version(self: Library) Version {
+ var v: Version = undefined;
+ c.FT_Library_Version(self.handle, &v.major, &v.minor, &v.patch);
+ return v;
+}
+
+/// Call FT_New_Face to open a font from a file.
+pub fn initFace(self: Library, path: [:0]const u8, index: i32) Error!Face {
+ var face: Face = undefined;
+ try intToError(c.FT_New_Face(
+ self.handle,
+ path.ptr,
+ index,
+ &face.handle,
+ ));
+ return face;
+}
+
+/// Call FT_Open_Face to open a font that has been loaded into memory.
+pub fn initMemoryFace(self: Library, data: []const u8, index: i32) Error!Face {
+ var face: Face = undefined;
+ try intToError(c.FT_New_Memory_Face(
+ self.handle,
+ data.ptr,
+ @intCast(data.len),
+ index,
+ &face.handle,
+ ));
+ return face;
+}
+
+/// Call when you're done with a loaded MM var.
+pub fn doneMMVar(self: Library, mm: *c.FT_MM_Var) void {
+ _ = c.FT_Done_MM_Var(self.handle, mm);
+}
+
+pub const Version = struct {
+ major: i32,
+ minor: i32,
+ patch: i32,
+
+ /// Convert the version to a string. The buffer should be able to
+ /// accommodate the size, recommended to be at least 8 chars wide.
+ /// The returned slice will be a slice of buf that contains the full
+ /// version string.
+ pub fn toString(self: Version, buf: []u8) ![]const u8 {
+ return try std.fmt.bufPrint(buf, "{d}.{d}.{d}", .{
+ self.major, self.minor, self.patch,
+ });
+ }
+};
+
+test "basics" {
+ const testing = std.testing;
+
+ var lib = try init();
+ defer lib.deinit();
+
+ const vsn = lib.version();
+ try testing.expect(vsn.major > 1);
+
+ var buf: [32]u8 = undefined;
+ _ = try vsn.toString(&buf);
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/build.zig b/vendor/libghostty-vt/pkg/freetype/build.zig
new file mode 100644
index 00000000..b85310a5
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/build.zig
@@ -0,0 +1,219 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+ const libpng_enabled = b.option(bool, "enable-libpng", "Build libpng") orelse false;
+
+ const module = b.addModule("freetype", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ var test_exe: ?*std.Build.Step.Compile = null;
+ if (target.query.isNative()) {
+ test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ const tests_run = b.addRunArtifact(test_exe.?);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+ }
+
+ module.addIncludePath(b.path(""));
+
+ if (b.systemIntegrationOption("freetype", .{})) {
+ module.linkSystemLibrary("freetype2", dynamic_link_opts);
+ if (test_exe) |exe| {
+ exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
+ }
+ } else {
+ const lib = try buildLib(b, module, .{
+ .target = target,
+ .optimize = optimize,
+
+ .libpng_enabled = libpng_enabled,
+
+ .dynamic_link_opts = dynamic_link_opts,
+ });
+
+ if (test_exe) |exe| {
+ exe.linkLibrary(lib);
+ }
+ }
+}
+
+fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
+ const target = options.target;
+ const optimize = options.optimize;
+
+ const libpng_enabled = options.libpng_enabled;
+
+ const lib = b.addLibrary(.{
+ .name = "freetype",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DFT2_BUILD_LIBRARY",
+
+ "-DFT_CONFIG_OPTION_SYSTEM_ZLIB=1",
+
+ "-fno-sanitize=undefined",
+ });
+ if (target.result.os.tag != .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_UNISTD_H",
+ "-DHAVE_FCNTL_H",
+ });
+ }
+
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ const dynamic_link_opts = options.dynamic_link_opts;
+
+ // Zlib
+ if (b.systemIntegrationOption("zlib", .{})) {
+ lib.linkSystemLibrary2("zlib", dynamic_link_opts);
+ } else {
+ const zlib_dep = b.dependency("zlib", .{ .target = target, .optimize = optimize });
+ lib.linkLibrary(zlib_dep.artifact("z"));
+ }
+
+ // Libpng
+ _ = b.systemIntegrationOption("libpng", .{}); // So it shows up in help
+ if (libpng_enabled) {
+ try flags.append(b.allocator, "-DFT_CONFIG_OPTION_USE_PNG=1");
+
+ if (b.systemIntegrationOption("libpng", .{})) {
+ lib.linkSystemLibrary2("libpng", dynamic_link_opts);
+ } else {
+ const libpng_dep = b.dependency(
+ "libpng",
+ .{ .target = target, .optimize = optimize },
+ );
+ lib.linkLibrary(libpng_dep.artifact("png"));
+ }
+ }
+
+ if (b.lazyDependency("freetype", .{})) |upstream| {
+ lib.addIncludePath(upstream.path("include"));
+ module.addIncludePath(upstream.path("include"));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+
+ switch (target.result.os.tag) {
+ .linux => lib.addCSourceFile(.{
+ .file = upstream.path("builds/unix/ftsystem.c"),
+ .flags = flags.items,
+ }),
+ .windows => lib.addCSourceFile(.{
+ .file = upstream.path("builds/windows/ftsystem.c"),
+ .flags = flags.items,
+ }),
+ else => lib.addCSourceFile(.{
+ .file = upstream.path("src/base/ftsystem.c"),
+ .flags = flags.items,
+ }),
+ }
+ switch (target.result.os.tag) {
+ .windows => {
+ lib.addCSourceFile(.{
+ .file = upstream.path("builds/windows/ftdebug.c"),
+ .flags = flags.items,
+ });
+ lib.addWin32ResourceFile(.{
+ .file = upstream.path("src/base/ftver.rc"),
+ });
+ },
+ else => lib.addCSourceFile(.{
+ .file = upstream.path("src/base/ftdebug.c"),
+ .flags = flags.items,
+ }),
+ }
+
+ lib.installHeader(b.path("freetype-zig.h"), "freetype-zig.h");
+ lib.installHeadersDirectory(
+ upstream.path("include"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ return lib;
+}
+
+const srcs: []const []const u8 = &.{
+ "src/autofit/autofit.c",
+ "src/base/ftbase.c",
+ "src/base/ftbbox.c",
+ "src/base/ftbdf.c",
+ "src/base/ftbitmap.c",
+ "src/base/ftcid.c",
+ "src/base/ftfstype.c",
+ "src/base/ftgasp.c",
+ "src/base/ftglyph.c",
+ "src/base/ftgxval.c",
+ "src/base/ftinit.c",
+ "src/base/ftmm.c",
+ "src/base/ftotval.c",
+ "src/base/ftpatent.c",
+ "src/base/ftpfr.c",
+ "src/base/ftstroke.c",
+ "src/base/ftsynth.c",
+ "src/base/fttype1.c",
+ "src/base/ftwinfnt.c",
+ "src/bdf/bdf.c",
+ "src/bzip2/ftbzip2.c",
+ "src/cache/ftcache.c",
+ "src/cff/cff.c",
+ "src/cid/type1cid.c",
+ "src/gzip/ftgzip.c",
+ "src/lzw/ftlzw.c",
+ "src/pcf/pcf.c",
+ "src/pfr/pfr.c",
+ "src/psaux/psaux.c",
+ "src/pshinter/pshinter.c",
+ "src/psnames/psnames.c",
+ "src/raster/raster.c",
+ "src/sdf/sdf.c",
+ "src/sfnt/sfnt.c",
+ "src/smooth/smooth.c",
+ "src/svg/svg.c",
+ "src/truetype/truetype.c",
+ "src/type1/type1.c",
+ "src/type42/type42.c",
+ "src/winfonts/winfnt.c",
+};
diff --git a/vendor/libghostty-vt/pkg/freetype/build.zig.zon b/vendor/libghostty-vt/pkg/freetype/build.zig.zon
new file mode 100644
index 00000000..8ed1516b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/build.zig.zon
@@ -0,0 +1,18 @@
+.{
+ .name = .freetype,
+ .version = "2.13.2",
+ .fingerprint = 0xac2059b6f7bbfe0a,
+ .paths = .{""},
+ .dependencies = .{
+ // freetype/freetype
+ .freetype = .{
+ .url = "https://deps.files.ghostty.org/freetype-1220b81f6ecfb3fd222f76cf9106fecfa6554ab07ec7fdc4124b9bb063ae2adf969d.tar.gz",
+ .hash = "N-V-__8AAKLKpwC4H27Ps_0iL3bPkQb-z6ZVSrB-x_3EEkub",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .libpng = .{ .path = "../libpng" },
+ .zlib = .{ .path = "../zlib" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/c.zig b/vendor/libghostty-vt/pkg/freetype/c.zig
new file mode 100644
index 00000000..9431abeb
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("freetype-zig.h");
+});
diff --git a/vendor/libghostty-vt/pkg/freetype/computations.zig b/vendor/libghostty-vt/pkg/freetype/computations.zig
new file mode 100644
index 00000000..fc9b2ccb
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/computations.zig
@@ -0,0 +1,8 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+/// Compute (a*b)/0x10000 with maximum accuracy. Its main use is to multiply
+/// a given value by a 16.16 fixed-point factor.
+pub fn mulFix(a: i32, b: i32) i32 {
+ return @intCast(c.FT_MulFix(a, b));
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/errors.zig b/vendor/libghostty-vt/pkg/freetype/errors.zig
new file mode 100644
index 00000000..9eddc057
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/errors.zig
@@ -0,0 +1,295 @@
+const c = @import("c.zig").c;
+
+// Thanks to Mach (https://github.com/hexops/mach) for this work, I didn't
+// do this manually. I wrote the other Freetype bindings by hand but this
+// one was... too tedius.
+
+pub const Error = error{
+ CannotOpenResource,
+ UnknownFileFormat,
+ InvalidFileFormat,
+ InvalidVersion,
+ LowerModuleVersion,
+ InvalidArgument,
+ UnimplementedFeature,
+ InvalidTable,
+ InvalidOffset,
+ ArrayTooLarge,
+ MissingModule,
+ MissingProperty,
+ InvalidGlyphIndex,
+ InvalidCharacterCode,
+ InvalidGlyphFormat,
+ CannotRenderGlyph,
+ InvalidOutline,
+ InvalidComposite,
+ TooManyHints,
+ InvalidPixelSize,
+ InvalidHandle,
+ InvalidLibraryHandle,
+ InvalidDriverHandle,
+ InvalidFaceHandle,
+ InvalidSizeHandle,
+ InvalidSlotHandle,
+ InvalidCharMapHandle,
+ InvalidCacheHandle,
+ InvalidStreamHandle,
+ TooManyDrivers,
+ TooManyExtensions,
+ OutOfMemory,
+ UnlistedObject,
+ CannotOpenStream,
+ InvalidStreamSeek,
+ InvalidStreamSkip,
+ InvalidStreamRead,
+ InvalidStreamOperation,
+ InvalidFrameOperation,
+ NestedFrameAccess,
+ InvalidFrameRead,
+ RasterUninitialized,
+ RasterCorrupted,
+ RasterOverflow,
+ RasterNegativeHeight,
+ TooManyCaches,
+ InvalidOpcode,
+ TooFewArguments,
+ StackOverflow,
+ CodeOverflow,
+ BadArgument,
+ DivideByZero,
+ InvalidReference,
+ DebugOpCode,
+ ENDFInExecStream,
+ NestedDEFS,
+ InvalidCodeRange,
+ ExecutionTooLong,
+ TooManyFunctionDefs,
+ TooManyInstructionDefs,
+ TableMissing,
+ HorizHeaderMissing,
+ LocationsMissing,
+ NameTableMissing,
+ CMapTableMissing,
+ HmtxTableMissing,
+ PostTableMissing,
+ InvalidHorizMetrics,
+ InvalidCharMapFormat,
+ InvalidPPem,
+ InvalidVertMetrics,
+ CouldNotFindContext,
+ InvalidPostTableFormat,
+ InvalidPostTable,
+ Syntax,
+ StackUnderflow,
+ Ignore,
+ NoUnicodeGlyphName,
+ MissingStartfontField,
+ MissingFontField,
+ MissingSizeField,
+ MissingFontboundingboxField,
+ MissingCharsField,
+ MissingStartcharField,
+ MissingEncodingField,
+ MissingBbxField,
+ BbxTooBig,
+ CorruptedFontHeader,
+ CorruptedFontGlyphs,
+ UnknownFreetypeError,
+};
+
+pub fn intToError(err: c_int) Error!void {
+ return switch (err) {
+ c.FT_Err_Ok => {},
+ c.FT_Err_Cannot_Open_Resource => Error.CannotOpenResource,
+ c.FT_Err_Unknown_File_Format => Error.UnknownFileFormat,
+ c.FT_Err_Invalid_File_Format => Error.InvalidFileFormat,
+ c.FT_Err_Invalid_Version => Error.InvalidVersion,
+ c.FT_Err_Lower_Module_Version => Error.LowerModuleVersion,
+ c.FT_Err_Invalid_Argument => Error.InvalidArgument,
+ c.FT_Err_Unimplemented_Feature => Error.UnimplementedFeature,
+ c.FT_Err_Invalid_Table => Error.InvalidTable,
+ c.FT_Err_Invalid_Offset => Error.InvalidOffset,
+ c.FT_Err_Array_Too_Large => Error.ArrayTooLarge,
+ c.FT_Err_Missing_Module => Error.MissingModule,
+ c.FT_Err_Missing_Property => Error.MissingProperty,
+ c.FT_Err_Invalid_Glyph_Index => Error.InvalidGlyphIndex,
+ c.FT_Err_Invalid_Character_Code => Error.InvalidCharacterCode,
+ c.FT_Err_Invalid_Glyph_Format => Error.InvalidGlyphFormat,
+ c.FT_Err_Cannot_Render_Glyph => Error.CannotRenderGlyph,
+ c.FT_Err_Invalid_Outline => Error.InvalidOutline,
+ c.FT_Err_Invalid_Composite => Error.InvalidComposite,
+ c.FT_Err_Too_Many_Hints => Error.TooManyHints,
+ c.FT_Err_Invalid_Pixel_Size => Error.InvalidPixelSize,
+ c.FT_Err_Invalid_Handle => Error.InvalidHandle,
+ c.FT_Err_Invalid_Library_Handle => Error.InvalidLibraryHandle,
+ c.FT_Err_Invalid_Driver_Handle => Error.InvalidDriverHandle,
+ c.FT_Err_Invalid_Face_Handle => Error.InvalidFaceHandle,
+ c.FT_Err_Invalid_Size_Handle => Error.InvalidSizeHandle,
+ c.FT_Err_Invalid_Slot_Handle => Error.InvalidSlotHandle,
+ c.FT_Err_Invalid_CharMap_Handle => Error.InvalidCharMapHandle,
+ c.FT_Err_Invalid_Cache_Handle => Error.InvalidCacheHandle,
+ c.FT_Err_Invalid_Stream_Handle => Error.InvalidStreamHandle,
+ c.FT_Err_Too_Many_Drivers => Error.TooManyDrivers,
+ c.FT_Err_Too_Many_Extensions => Error.TooManyExtensions,
+ c.FT_Err_Out_Of_Memory => Error.OutOfMemory,
+ c.FT_Err_Unlisted_Object => Error.UnlistedObject,
+ c.FT_Err_Cannot_Open_Stream => Error.CannotOpenStream,
+ c.FT_Err_Invalid_Stream_Seek => Error.InvalidStreamSeek,
+ c.FT_Err_Invalid_Stream_Skip => Error.InvalidStreamSkip,
+ c.FT_Err_Invalid_Stream_Read => Error.InvalidStreamRead,
+ c.FT_Err_Invalid_Stream_Operation => Error.InvalidStreamOperation,
+ c.FT_Err_Invalid_Frame_Operation => Error.InvalidFrameOperation,
+ c.FT_Err_Nested_Frame_Access => Error.NestedFrameAccess,
+ c.FT_Err_Invalid_Frame_Read => Error.InvalidFrameRead,
+ c.FT_Err_Raster_Uninitialized => Error.RasterUninitialized,
+ c.FT_Err_Raster_Corrupted => Error.RasterCorrupted,
+ c.FT_Err_Raster_Overflow => Error.RasterOverflow,
+ c.FT_Err_Raster_Negative_Height => Error.RasterNegativeHeight,
+ c.FT_Err_Too_Many_Caches => Error.TooManyCaches,
+ c.FT_Err_Invalid_Opcode => Error.InvalidOpcode,
+ c.FT_Err_Too_Few_Arguments => Error.TooFewArguments,
+ c.FT_Err_Stack_Overflow => Error.StackOverflow,
+ c.FT_Err_Code_Overflow => Error.CodeOverflow,
+ c.FT_Err_Bad_Argument => Error.BadArgument,
+ c.FT_Err_Divide_By_Zero => Error.DivideByZero,
+ c.FT_Err_Invalid_Reference => Error.InvalidReference,
+ c.FT_Err_Debug_OpCode => Error.DebugOpCode,
+ c.FT_Err_ENDF_In_Exec_Stream => Error.ENDFInExecStream,
+ c.FT_Err_Nested_DEFS => Error.NestedDEFS,
+ c.FT_Err_Invalid_CodeRange => Error.InvalidCodeRange,
+ c.FT_Err_Execution_Too_Long => Error.ExecutionTooLong,
+ c.FT_Err_Too_Many_Function_Defs => Error.TooManyFunctionDefs,
+ c.FT_Err_Too_Many_Instruction_Defs => Error.TooManyInstructionDefs,
+ c.FT_Err_Table_Missing => Error.TableMissing,
+ c.FT_Err_Horiz_Header_Missing => Error.HorizHeaderMissing,
+ c.FT_Err_Locations_Missing => Error.LocationsMissing,
+ c.FT_Err_Name_Table_Missing => Error.NameTableMissing,
+ c.FT_Err_CMap_Table_Missing => Error.CMapTableMissing,
+ c.FT_Err_Hmtx_Table_Missing => Error.HmtxTableMissing,
+ c.FT_Err_Post_Table_Missing => Error.PostTableMissing,
+ c.FT_Err_Invalid_Horiz_Metrics => Error.InvalidHorizMetrics,
+ c.FT_Err_Invalid_CharMap_Format => Error.InvalidCharMapFormat,
+ c.FT_Err_Invalid_PPem => Error.InvalidPPem,
+ c.FT_Err_Invalid_Vert_Metrics => Error.InvalidVertMetrics,
+ c.FT_Err_Could_Not_Find_Context => Error.CouldNotFindContext,
+ c.FT_Err_Invalid_Post_Table_Format => Error.InvalidPostTableFormat,
+ c.FT_Err_Invalid_Post_Table => Error.InvalidPostTable,
+ c.FT_Err_Syntax_Error => Error.Syntax,
+ c.FT_Err_Stack_Underflow => Error.StackUnderflow,
+ c.FT_Err_Ignore => Error.Ignore,
+ c.FT_Err_No_Unicode_Glyph_Name => Error.NoUnicodeGlyphName,
+ c.FT_Err_Missing_Startfont_Field => Error.MissingStartfontField,
+ c.FT_Err_Missing_Font_Field => Error.MissingFontField,
+ c.FT_Err_Missing_Size_Field => Error.MissingSizeField,
+ c.FT_Err_Missing_Fontboundingbox_Field => Error.MissingFontboundingboxField,
+ c.FT_Err_Missing_Chars_Field => Error.MissingCharsField,
+ c.FT_Err_Missing_Startchar_Field => Error.MissingStartcharField,
+ c.FT_Err_Missing_Encoding_Field => Error.MissingEncodingField,
+ c.FT_Err_Missing_Bbx_Field => Error.MissingBbxField,
+ c.FT_Err_Bbx_Too_Big => Error.BbxTooBig,
+ c.FT_Err_Corrupted_Font_Header => Error.CorruptedFontHeader,
+ c.FT_Err_Corrupted_Font_Glyphs => Error.CorruptedFontGlyphs,
+ else => Error.UnknownFreetypeError,
+ };
+}
+
+pub fn errorToInt(err: Error) c_int {
+ return switch (err) {
+ Error.CannotOpenResource => c.FT_Err_Cannot_Open_Resource,
+ Error.UnknownFileFormat => c.FT_Err_Unknown_File_Format,
+ Error.InvalidFileFormat => c.FT_Err_Invalid_File_Format,
+ Error.InvalidVersion => c.FT_Err_Invalid_Version,
+ Error.LowerModuleVersion => c.FT_Err_Lower_Module_Version,
+ Error.InvalidArgument => c.FT_Err_Invalid_Argument,
+ Error.UnimplementedFeature => c.FT_Err_Unimplemented_Feature,
+ Error.InvalidTable => c.FT_Err_Invalid_Table,
+ Error.InvalidOffset => c.FT_Err_Invalid_Offset,
+ Error.ArrayTooLarge => c.FT_Err_Array_Too_Large,
+ Error.MissingModule => c.FT_Err_Missing_Module,
+ Error.MissingProperty => c.FT_Err_Missing_Property,
+ Error.InvalidGlyphIndex => c.FT_Err_Invalid_Glyph_Index,
+ Error.InvalidCharacterCode => c.FT_Err_Invalid_Character_Code,
+ Error.InvalidGlyphFormat => c.FT_Err_Invalid_Glyph_Format,
+ Error.CannotRenderGlyph => c.FT_Err_Cannot_Render_Glyph,
+ Error.InvalidOutline => c.FT_Err_Invalid_Outline,
+ Error.InvalidComposite => c.FT_Err_Invalid_Composite,
+ Error.TooManyHints => c.FT_Err_Too_Many_Hints,
+ Error.InvalidPixelSize => c.FT_Err_Invalid_Pixel_Size,
+ Error.InvalidHandle => c.FT_Err_Invalid_Handle,
+ Error.InvalidLibraryHandle => c.FT_Err_Invalid_Library_Handle,
+ Error.InvalidDriverHandle => c.FT_Err_Invalid_Driver_Handle,
+ Error.InvalidFaceHandle => c.FT_Err_Invalid_Face_Handle,
+ Error.InvalidSizeHandle => c.FT_Err_Invalid_Size_Handle,
+ Error.InvalidSlotHandle => c.FT_Err_Invalid_Slot_Handle,
+ Error.InvalidCharMapHandle => c.FT_Err_Invalid_CharMap_Handle,
+ Error.InvalidCacheHandle => c.FT_Err_Invalid_Cache_Handle,
+ Error.InvalidStreamHandle => c.FT_Err_Invalid_Stream_Handle,
+ Error.TooManyDrivers => c.FT_Err_Too_Many_Drivers,
+ Error.TooManyExtensions => c.FT_Err_Too_Many_Extensions,
+ Error.OutOfMemory => c.FT_Err_Out_Of_Memory,
+ Error.UnlistedObject => c.FT_Err_Unlisted_Object,
+ Error.CannotOpenStream => c.FT_Err_Cannot_Open_Stream,
+ Error.InvalidStreamSeek => c.FT_Err_Invalid_Stream_Seek,
+ Error.InvalidStreamSkip => c.FT_Err_Invalid_Stream_Skip,
+ Error.InvalidStreamRead => c.FT_Err_Invalid_Stream_Read,
+ Error.InvalidStreamOperation => c.FT_Err_Invalid_Stream_Operation,
+ Error.InvalidFrameOperation => c.FT_Err_Invalid_Frame_Operation,
+ Error.NestedFrameAccess => c.FT_Err_Nested_Frame_Access,
+ Error.InvalidFrameRead => c.FT_Err_Invalid_Frame_Read,
+ Error.RasterUninitialized => c.FT_Err_Raster_Uninitialized,
+ Error.RasterCorrupted => c.FT_Err_Raster_Corrupted,
+ Error.RasterOverflow => c.FT_Err_Raster_Overflow,
+ Error.RasterNegativeHeight => c.FT_Err_Raster_Negative_Height,
+ Error.TooManyCaches => c.FT_Err_Too_Many_Caches,
+ Error.InvalidOpcode => c.FT_Err_Invalid_Opcode,
+ Error.TooFewArguments => c.FT_Err_Too_Few_Arguments,
+ Error.StackOverflow => c.FT_Err_Stack_Overflow,
+ Error.CodeOverflow => c.FT_Err_Code_Overflow,
+ Error.BadArgument => c.FT_Err_Bad_Argument,
+ Error.DivideByZero => c.FT_Err_Divide_By_Zero,
+ Error.InvalidReference => c.FT_Err_Invalid_Reference,
+ Error.DebugOpCode => c.FT_Err_Debug_OpCode,
+ Error.ENDFInExecStream => c.FT_Err_ENDF_In_Exec_Stream,
+ Error.NestedDEFS => c.FT_Err_Nested_DEFS,
+ Error.InvalidCodeRange => c.FT_Err_Invalid_CodeRange,
+ Error.ExecutionTooLong => c.FT_Err_Execution_Too_Long,
+ Error.TooManyFunctionDefs => c.FT_Err_Too_Many_Function_Defs,
+ Error.TooManyInstructionDefs => c.FT_Err_Too_Many_Instruction_Defs,
+ Error.TableMissing => c.FT_Err_Table_Missing,
+ Error.HorizHeaderMissing => c.FT_Err_Horiz_Header_Missing,
+ Error.LocationsMissing => c.FT_Err_Locations_Missing,
+ Error.NameTableMissing => c.FT_Err_Name_Table_Missing,
+ Error.CMapTableMissing => c.FT_Err_CMap_Table_Missing,
+ Error.HmtxTableMissing => c.FT_Err_Hmtx_Table_Missing,
+ Error.PostTableMissing => c.FT_Err_Post_Table_Missing,
+ Error.InvalidHorizMetrics => c.FT_Err_Invalid_Horiz_Metrics,
+ Error.InvalidCharMapFormat => c.FT_Err_Invalid_CharMap_Format,
+ Error.InvalidPPem => c.FT_Err_Invalid_PPem,
+ Error.InvalidVertMetrics => c.FT_Err_Invalid_Vert_Metrics,
+ Error.CouldNotFindContext => c.FT_Err_Could_Not_Find_Context,
+ Error.InvalidPostTableFormat => c.FT_Err_Invalid_Post_Table_Format,
+ Error.InvalidPostTable => c.FT_Err_Invalid_Post_Table,
+ Error.Syntax => c.FT_Err_Syntax_Error,
+ Error.StackUnderflow => c.FT_Err_Stack_Underflow,
+ Error.Ignore => c.FT_Err_Ignore,
+ Error.NoUnicodeGlyphName => c.FT_Err_No_Unicode_Glyph_Name,
+ Error.MissingStartfontField => c.FT_Err_Missing_Startfont_Field,
+ Error.MissingFontField => c.FT_Err_Missing_Font_Field,
+ Error.MissingSizeField => c.FT_Err_Missing_Size_Field,
+ Error.MissingFontboundingboxField => c.FT_Err_Missing_Fontboundingbox_Field,
+ Error.MissingCharsField => c.FT_Err_Missing_Chars_Field,
+ Error.MissingStartcharField => c.FT_Err_Missing_Startchar_Field,
+ Error.MissingEncodingField => c.FT_Err_Missing_Encoding_Field,
+ Error.MissingBbxField => c.FT_Err_Missing_Bbx_Field,
+ Error.BbxTooBig => c.FT_Err_Bbx_Too_Big,
+ Error.CorruptedFontHeader => c.FT_Err_Corrupted_Font_Header,
+ Error.CorruptedFontGlyphs => c.FT_Err_Corrupted_Font_Glyphs,
+ };
+}
+
+test "error conversion" {
+ const expectError = @import("std").testing.expectError;
+
+ try intToError(c.FT_Err_Ok);
+ try expectError(Error.OutOfMemory, intToError(c.FT_Err_Out_Of_Memory));
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/face.zig b/vendor/libghostty-vt/pkg/freetype/face.zig
new file mode 100644
index 00000000..cd949e35
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/face.zig
@@ -0,0 +1,385 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const Library = @import("Library.zig");
+const Tag = @import("tag.zig").Tag;
+const Error = errors.Error;
+const intToError = errors.intToError;
+
+pub const Face = struct {
+ handle: c.FT_Face,
+
+ pub fn deinit(self: Face) void {
+ _ = c.FT_Done_Face(self.handle);
+ }
+
+ /// Increment the counter of the face.
+ pub fn ref(self: Face) void {
+ _ = c.FT_Reference_Face(self.handle);
+ }
+
+ /// A macro that returns true whenever a face object contains some
+ /// embedded bitmaps. See the available_sizes field of the FT_FaceRec structure.
+ pub fn hasFixedSizes(self: Face) bool {
+ return c.FT_HAS_FIXED_SIZES(self.handle);
+ }
+
+ /// A macro that returns true whenever a face object contains tables for
+ /// color glyphs.
+ pub fn hasColor(self: Face) bool {
+ return c.FT_HAS_COLOR(self.handle);
+ }
+
+ /// A macro that returns true whenever a face object contains an ‘sbix’
+ /// OpenType table and outline glyphs.
+ pub fn hasSBIX(self: Face) bool {
+ return c.FT_HAS_SBIX(self.handle);
+ }
+
+ /// A macro that returns true whenever a face object contains some
+ /// multiple masters.
+ pub fn hasMultipleMasters(self: Face) bool {
+ return c.FT_HAS_MULTIPLE_MASTERS(self.handle);
+ }
+
+ /// A macro that returns true whenever a face object contains a scalable
+ /// font face (true for TrueType, Type 1, Type 42, CID, OpenType/CFF,
+ /// and PFR font formats).
+ pub fn isScalable(self: Face) bool {
+ return c.FT_IS_SCALABLE(self.handle);
+ }
+
+ /// Select a given charmap by its encoding tag (as listed in freetype.h).
+ pub fn selectCharmap(self: Face, encoding: Encoding) Error!void {
+ return intToError(c.FT_Select_Charmap(self.handle, @intCast(@intFromEnum(encoding))));
+ }
+
+ /// Call FT_Request_Size to request the nominal size (in points).
+ pub fn setCharSize(
+ self: Face,
+ char_width: i32,
+ char_height: i32,
+ horz_resolution: u16,
+ vert_resolution: u16,
+ ) Error!void {
+ return intToError(c.FT_Set_Char_Size(
+ self.handle,
+ char_width,
+ char_height,
+ horz_resolution,
+ vert_resolution,
+ ));
+ }
+
+ /// Select a bitmap strike. To be more precise, this function sets the
+ /// scaling factors of the active FT_Size object in a face so that bitmaps
+ /// from this particular strike are taken by FT_Load_Glyph and friends.
+ pub fn selectSize(self: Face, idx: i32) Error!void {
+ return intToError(c.FT_Select_Size(self.handle, idx));
+ }
+
+ /// Return the glyph index of a given character code. This function uses
+ /// the currently selected charmap to do the mapping.
+ pub fn getCharIndex(self: Face, char: u32) ?u32 {
+ const i = c.FT_Get_Char_Index(self.handle, char);
+ return if (i == 0) null else i;
+ }
+
+ /// Load a glyph into the glyph slot of a face object.
+ pub fn loadGlyph(self: Face, glyph_index: u32, load_flags: LoadFlags) Error!void {
+ return intToError(c.FT_Load_Glyph(
+ self.handle,
+ glyph_index,
+ @bitCast(load_flags),
+ ));
+ }
+
+ /// Convert a given glyph image to a bitmap.
+ pub fn renderGlyph(self: Face, render_mode: RenderMode) Error!void {
+ return intToError(c.FT_Render_Glyph(
+ self.handle.*.glyph,
+ @intCast(@intFromEnum(render_mode)),
+ ));
+ }
+
+ /// Return a pointer to a given SFNT table stored within a face.
+ pub fn getSfntTable(self: Face, comptime tag: SfntTag) ?*tag.DataType() {
+ return @ptrCast(@alignCast(c.FT_Get_Sfnt_Table(
+ self.handle,
+ @intFromEnum(tag),
+ )));
+ }
+
+ /// Retrieve the number of name strings in the SFNT ‘name’ table.
+ pub fn getSfntNameCount(self: Face) usize {
+ return @intCast(c.FT_Get_Sfnt_Name_Count(self.handle));
+ }
+
+ /// Retrieve a string of the SFNT ‘name’ table for a given index.
+ pub fn getSfntName(self: Face, i: usize) Error!c.FT_SfntName {
+ var name: c.FT_SfntName = undefined;
+ const res = c.FT_Get_Sfnt_Name(self.handle, @intCast(i), &name);
+ return if (intToError(res)) |_| name else |err| err;
+ }
+
+ /// Load any SFNT font table into client memory.
+ pub fn loadSfntTable(
+ self: Face,
+ alloc: Allocator,
+ tag: Tag,
+ ) (Allocator.Error || Error)!?[]u8 {
+ const tag_c: c_ulong = @intCast(@as(u32, @bitCast(tag)));
+
+ // Get the length of the table in bytes
+ var len: c_ulong = 0;
+ var res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, null, &len);
+ _ = intToError(res) catch |err| return err;
+
+ // If our length is zero we don't have a table.
+ if (len == 0) return null;
+
+ // Allocate a buffer to hold the table and load it
+ const buf = try alloc.alloc(u8, len);
+ errdefer alloc.free(buf);
+ res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, buf.ptr, &len);
+ _ = intToError(res) catch |err| return err;
+
+ return buf;
+ }
+
+ /// Check whether a given SFNT table is available in a face.
+ pub fn hasSfntTable(self: Face, tag: Tag) bool {
+ const tag_c: c_ulong = @intCast(@as(u32, @bitCast(tag)));
+ var len: c_ulong = 0;
+ const res = c.FT_Load_Sfnt_Table(self.handle, tag_c, 0, null, &len);
+ _ = intToError(res) catch return false;
+ return len != 0;
+ }
+
+ /// Retrieve the font variation descriptor for a font.
+ pub fn getMMVar(self: Face) Error!*c.FT_MM_Var {
+ var result: *c.FT_MM_Var = undefined;
+ const res = c.FT_Get_MM_Var(self.handle, @ptrCast(&result));
+ return if (intToError(res)) |_| result else |err| err;
+ }
+
+ /// Get the design coordinates of the currently selected interpolated font.
+ pub fn getVarDesignCoordinates(self: Face, coords: []c.FT_Fixed) Error!void {
+ const res = c.FT_Get_Var_Design_Coordinates(
+ self.handle,
+ @intCast(coords.len),
+ coords.ptr,
+ );
+ return intToError(res);
+ }
+
+ /// Choose an interpolated font design through design coordinates.
+ pub fn setVarDesignCoordinates(self: Face, coords: []c.FT_Fixed) Error!void {
+ const res = c.FT_Set_Var_Design_Coordinates(
+ self.handle,
+ @intCast(coords.len),
+ coords.ptr,
+ );
+ return intToError(res);
+ }
+
+ /// Set the transformation that is applied to glyph images when they are
+ /// loaded into a glyph slot through FT_Load_Glyph.
+ pub fn setTransform(
+ self: Face,
+ matrix: ?*const c.FT_Matrix,
+ delta: ?*const c.FT_Vector,
+ ) void {
+ c.FT_Set_Transform(
+ self.handle,
+ @ptrCast(@constCast(matrix)),
+ @ptrCast(@constCast(delta)),
+ );
+ }
+};
+
+/// An enumeration to specify indices of SFNT tables loaded and parsed by
+/// FreeType during initialization of an SFNT font. Used in the
+/// FT_Get_Sfnt_Table API function.
+pub const SfntTag = enum(c_int) {
+ head = c.FT_SFNT_HEAD,
+ maxp = c.FT_SFNT_MAXP,
+ os2 = c.FT_SFNT_OS2,
+ hhea = c.FT_SFNT_HHEA,
+ vhea = c.FT_SFNT_VHEA,
+ post = c.FT_SFNT_POST,
+ pclt = c.FT_SFNT_PCLT,
+
+ /// The data type for a given sfnt tag.
+ pub fn DataType(comptime self: SfntTag) type {
+ return switch (self) {
+ .os2 => c.TT_OS2,
+ .head => c.TT_Header,
+ .post => c.TT_Postscript,
+ .hhea => c.TT_HoriHeader,
+ else => unreachable, // As-needed...
+ };
+ }
+};
+
+/// An enumeration to specify character sets supported by charmaps. Used in the
+/// FT_Select_Charmap API function.
+pub const Encoding = enum(u31) {
+ none = c.FT_ENCODING_NONE,
+ ms_symbol = c.FT_ENCODING_MS_SYMBOL,
+ unicode = c.FT_ENCODING_UNICODE,
+ sjis = c.FT_ENCODING_SJIS,
+ prc = c.FT_ENCODING_PRC,
+ big5 = c.FT_ENCODING_BIG5,
+ wansung = c.FT_ENCODING_WANSUNG,
+ johab = c.FT_ENCODING_JOHAB,
+ adobe_standard = c.FT_ENCODING_ADOBE_STANDARD,
+ adobe_expert = c.FT_ENCODING_ADOBE_EXPERT,
+ adobe_custom = c.FT_ENCODING_ADOBE_CUSTOM,
+ adobe_latin_1 = c.FT_ENCODING_ADOBE_LATIN_1,
+ old_latin_2 = c.FT_ENCODING_OLD_LATIN_2,
+ apple_roman = c.FT_ENCODING_APPLE_ROMAN,
+};
+
+/// https://freetype.org/freetype2/docs/reference/ft2-glyph_retrieval.html#ft_render_mode
+pub const RenderMode = enum(c_uint) {
+ normal = c.FT_RENDER_MODE_NORMAL,
+ light = c.FT_RENDER_MODE_LIGHT,
+ mono = c.FT_RENDER_MODE_MONO,
+ lcd = c.FT_RENDER_MODE_LCD,
+ lcd_v = c.FT_RENDER_MODE_LCD_V,
+ sdf = c.FT_RENDER_MODE_SDF,
+};
+
+/// A collection of flags for FT_Load_Glyph that indicate
+/// what kind of operations to perform during glyph loading.
+///
+/// Some of these flags are not included in the official FreeType
+/// documentation, but are nevertheless present and named in the
+/// header, so the names have been copied from there.
+pub const LoadFlags = packed struct(c_int) {
+ no_scale: bool = false,
+ no_hinting: bool = false,
+ render: bool = false,
+ no_bitmap: bool = false,
+ vertical_layout: bool = false,
+ force_autohint: bool = false,
+ crop_bitmap: bool = false,
+ pedantic: bool = false,
+ advance_only: bool = false,
+ ignore_global_advance_width: bool = false,
+ no_recurse: bool = false,
+ ignore_transform: bool = false,
+ monochrome: bool = false,
+ linear_design: bool = false,
+ sbits_only: bool = false,
+ no_autohint: bool = false,
+ target: Target = .normal,
+ color: bool = false,
+ compute_metrics: bool = false,
+ bitmap_metrics_only: bool = false,
+ svg_only: bool = false,
+ no_svg: bool = false,
+ _padding: u7 = 0,
+
+ pub const Target = enum(u4) {
+ normal = 0,
+ light = 1,
+ mono = 2,
+ lcd = 3,
+ lcd_v = 4,
+ };
+
+ test "bitcast" {
+ const testing = std.testing;
+
+ const cval: i32 = c.FT_LOAD_RENDER | c.FT_LOAD_PEDANTIC | c.FT_LOAD_COLOR;
+ const flags = @as(LoadFlags, @bitCast(cval));
+ try testing.expect(!flags.no_hinting);
+ try testing.expect(flags.render);
+ try testing.expect(flags.pedantic);
+ try testing.expect(flags.color);
+
+ // Verify bit alignment (for bit 9)
+ const cval2: i32 = c.FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH;
+ const flags2 = @as(LoadFlags, @bitCast(cval2));
+ try testing.expect(flags2.ignore_global_advance_width);
+ try testing.expect(!flags2.no_recurse);
+ }
+
+ test "all flags individually" {
+ const testing = std.testing;
+
+ try testing.expectEqual(
+ c.FT_LOAD_DEFAULT,
+ @as(c_int, @bitCast(LoadFlags{})),
+ );
+
+ inline for ([_]struct { c_int, []const u8 }{
+ .{ c.FT_LOAD_NO_SCALE, "no_scale" },
+ .{ c.FT_LOAD_NO_HINTING, "no_hinting" },
+ .{ c.FT_LOAD_RENDER, "render" },
+ .{ c.FT_LOAD_NO_BITMAP, "no_bitmap" },
+ .{ c.FT_LOAD_VERTICAL_LAYOUT, "vertical_layout" },
+ .{ c.FT_LOAD_FORCE_AUTOHINT, "force_autohint" },
+ .{ c.FT_LOAD_CROP_BITMAP, "crop_bitmap" },
+ .{ c.FT_LOAD_PEDANTIC, "pedantic" },
+ .{ c.FT_LOAD_ADVANCE_ONLY, "advance_only" },
+ .{ c.FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH, "ignore_global_advance_width" },
+ .{ c.FT_LOAD_NO_RECURSE, "no_recurse" },
+ .{ c.FT_LOAD_IGNORE_TRANSFORM, "ignore_transform" },
+ .{ c.FT_LOAD_MONOCHROME, "monochrome" },
+ .{ c.FT_LOAD_LINEAR_DESIGN, "linear_design" },
+ .{ c.FT_LOAD_SBITS_ONLY, "sbits_only" },
+ .{ c.FT_LOAD_NO_AUTOHINT, "no_autohint" },
+ .{ c.FT_LOAD_COLOR, "color" },
+ .{ c.FT_LOAD_COMPUTE_METRICS, "compute_metrics" },
+ .{ c.FT_LOAD_BITMAP_METRICS_ONLY, "bitmap_metrics_only" },
+ .{ c.FT_LOAD_SVG_ONLY, "svg_only" },
+ .{ c.FT_LOAD_NO_SVG, "no_svg" },
+ }) |pair| {
+ var flags: LoadFlags = .{};
+ @field(flags, pair[1]) = true;
+ try testing.expectEqual(pair[0], @as(c_int, @bitCast(flags)));
+ }
+ }
+
+ test "all load targets" {
+ const testing = std.testing;
+
+ inline for ([_]struct { c_int, Target }{
+ .{ c.FT_LOAD_TARGET_NORMAL, .normal },
+ .{ c.FT_LOAD_TARGET_LIGHT, .light },
+ .{ c.FT_LOAD_TARGET_MONO, .mono },
+ .{ c.FT_LOAD_TARGET_LCD, .lcd },
+ .{ c.FT_LOAD_TARGET_LCD_V, .lcd_v },
+ }) |pair| {
+ const flags: LoadFlags = .{ .target = pair[1] };
+ try testing.expectEqual(pair[0], @as(c_int, @bitCast(flags)));
+ }
+ }
+};
+
+test "loading memory font" {
+ const testing = std.testing;
+ const font_data = @import("test.zig").font_regular;
+
+ var lib = try Library.init();
+ defer lib.deinit();
+ var face = try lib.initMemoryFace(font_data, 0);
+ defer face.deinit();
+
+ // Try APIs
+ try face.selectCharmap(.unicode);
+ try testing.expect(!face.hasFixedSizes());
+ try face.setCharSize(12, 0, 0, 0);
+
+ // Try loading
+ const idx = face.getCharIndex('A').?;
+ try face.loadGlyph(idx, .{});
+
+ // Try getting a truetype table
+ const os2 = face.getSfntTable(.os2);
+ try testing.expect(os2 != null);
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/freetype-zig.h b/vendor/libghostty-vt/pkg/freetype/freetype-zig.h
new file mode 100644
index 00000000..dcc65e51
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/freetype-zig.h
@@ -0,0 +1,9 @@
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_TRUETYPE_TABLES_H
+#include <freetype/ftmm.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftsnames.h>
+#include <freetype/ttnameid.h>
+#include <freetype/ftbitmap.h>
+#include <freetype/ftbbox.h>
diff --git a/vendor/libghostty-vt/pkg/freetype/main.zig b/vendor/libghostty-vt/pkg/freetype/main.zig
new file mode 100644
index 00000000..6ec81818
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/main.zig
@@ -0,0 +1,18 @@
+const computations = @import("computations.zig");
+const errors = @import("errors.zig");
+const face = @import("face.zig");
+const tag = @import("tag.zig");
+
+pub const c = @import("c.zig").c;
+pub const testing = @import("test.zig");
+pub const Library = @import("Library.zig");
+
+pub const Error = errors.Error;
+pub const Face = face.Face;
+pub const LoadFlags = face.LoadFlags;
+pub const Tag = tag.Tag;
+pub const mulFix = computations.mulFix;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/freetype/res/FiraCode-Regular.ttf b/vendor/libghostty-vt/pkg/freetype/res/FiraCode-Regular.ttf
new file mode 100755
index 00000000..bd736851
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/res/FiraCode-Regular.ttf
Binary files differ
diff --git a/vendor/libghostty-vt/pkg/freetype/tag.zig b/vendor/libghostty-vt/pkg/freetype/tag.zig
new file mode 100644
index 00000000..32dab2a0
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/tag.zig
@@ -0,0 +1,17 @@
+/// FT_Tag
+pub const Tag = packed struct(u32) {
+ d: u8,
+ c: u8,
+ b: u8,
+ a: u8,
+
+ pub fn init(v: *const [4]u8) Tag {
+ return .{ .a = v[0], .b = v[1], .c = v[2], .d = v[3] };
+ }
+
+ /// Converts the ID to a string. The return value is only valid
+ /// for the lifetime of the self pointer.
+ pub fn str(self: Tag) [4]u8 {
+ return .{ self.a, self.b, self.c, self.d };
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/freetype/test.zig b/vendor/libghostty-vt/pkg/freetype/test.zig
new file mode 100644
index 00000000..866c6f2a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/freetype/test.zig
@@ -0,0 +1 @@
+pub const font_regular = @embedFile("res/FiraCode-Regular.ttf");
diff --git a/vendor/libghostty-vt/pkg/glslang/build.zig b/vendor/libghostty-vt/pkg/glslang/build.zig
new file mode 100644
index 00000000..1dc82a6e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/build.zig
@@ -0,0 +1,169 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const module = b.addModule("glslang", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const upstream = b.lazyDependency("glslang", .{});
+ const lib = try buildGlslang(b, upstream, target, optimize);
+ b.installArtifact(lib);
+
+ if (upstream) |v| module.addIncludePath(v.path(""));
+ module.addIncludePath(b.path("override"));
+
+ if (target.query.isNative()) {
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ test_exe.linkLibrary(lib);
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+
+ // Uncomment this if we're debugging tests
+ // b.installArtifact(test_exe);
+ }
+}
+
+fn buildGlslang(
+ b: *std.Build,
+ upstream_: ?*std.Build.Dependency,
+ target: std.Build.ResolvedTarget,
+ optimize: std.builtin.OptimizeMode,
+) !*std.Build.Step.Compile {
+ const lib = b.addLibrary(.{
+ .name = "glslang",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+ if (upstream_) |upstream| lib.addIncludePath(upstream.path(""));
+ lib.addIncludePath(b.path("override"));
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-fno-sanitize=undefined",
+ "-fno-sanitize-trap=undefined",
+ });
+ // MSVC requires explicit std specification otherwise C++17 features
+ // like std::variant, std::filesystem, and inline variables are
+ // guarded behind _HAS_CXX17.
+ try flags.append(b.allocator, "-std=c++17");
+
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ if (upstream_) |upstream| {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ // GenericCodeGen
+ "glslang/GenericCodeGen/CodeGen.cpp",
+ "glslang/GenericCodeGen/Link.cpp",
+
+ // MachineIndependent
+ //"MachineIndependent/glslang.y",
+ "glslang/MachineIndependent/glslang_tab.cpp",
+ "glslang/MachineIndependent/attribute.cpp",
+ "glslang/MachineIndependent/Constant.cpp",
+ "glslang/MachineIndependent/iomapper.cpp",
+ "glslang/MachineIndependent/InfoSink.cpp",
+ "glslang/MachineIndependent/Initialize.cpp",
+ "glslang/MachineIndependent/IntermTraverse.cpp",
+ "glslang/MachineIndependent/Intermediate.cpp",
+ "glslang/MachineIndependent/ParseContextBase.cpp",
+ "glslang/MachineIndependent/ParseHelper.cpp",
+ "glslang/MachineIndependent/PoolAlloc.cpp",
+ "glslang/MachineIndependent/RemoveTree.cpp",
+ "glslang/MachineIndependent/Scan.cpp",
+ "glslang/MachineIndependent/ShaderLang.cpp",
+ "glslang/MachineIndependent/SpirvIntrinsics.cpp",
+ "glslang/MachineIndependent/SymbolTable.cpp",
+ "glslang/MachineIndependent/Versions.cpp",
+ "glslang/MachineIndependent/intermOut.cpp",
+ "glslang/MachineIndependent/limits.cpp",
+ "glslang/MachineIndependent/linkValidate.cpp",
+ "glslang/MachineIndependent/parseConst.cpp",
+ "glslang/MachineIndependent/reflection.cpp",
+ "glslang/MachineIndependent/preprocessor/Pp.cpp",
+ "glslang/MachineIndependent/preprocessor/PpAtom.cpp",
+ "glslang/MachineIndependent/preprocessor/PpContext.cpp",
+ "glslang/MachineIndependent/preprocessor/PpScanner.cpp",
+ "glslang/MachineIndependent/preprocessor/PpTokens.cpp",
+ "glslang/MachineIndependent/propagateNoContraction.cpp",
+
+ // C Interface
+ "glslang/CInterface/glslang_c_interface.cpp",
+
+ // ResourceLimits
+ "glslang/ResourceLimits/ResourceLimits.cpp",
+ "glslang/ResourceLimits/resource_limits_c.cpp",
+
+ // SPIRV
+ "SPIRV/GlslangToSpv.cpp",
+ "SPIRV/InReadableOrder.cpp",
+ "SPIRV/Logger.cpp",
+ "SPIRV/SpvBuilder.cpp",
+ "SPIRV/SpvPostProcess.cpp",
+ "SPIRV/doc.cpp",
+ "SPIRV/disassemble.cpp",
+ "SPIRV/CInterface/spirv_c_interface.cpp",
+ },
+ });
+
+ if (target.result.os.tag != .windows) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ "glslang/OSDependent/Unix/ossource.cpp",
+ },
+ });
+ } else {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ "glslang/OSDependent/Windows/ossource.cpp",
+ },
+ });
+ }
+
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ return lib;
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/build.zig.zon b/vendor/libghostty-vt/pkg/glslang/build.zig.zon
new file mode 100644
index 00000000..252237e5
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/build.zig.zon
@@ -0,0 +1,16 @@
+.{
+ .name = .glslang,
+ .version = "14.2.0",
+ .fingerprint = 0x274a35558e2e504,
+ .paths = .{""},
+ .dependencies = .{
+ // KhronosGroup/glslang
+ .glslang = .{
+ .url = "https://deps.files.ghostty.org/glslang-12201278a1a05c0ce0b6eb6026c65cd3e9247aa041b1c260324bf29cee559dd23ba1.tar.gz",
+ .hash = "N-V-__8AABzkUgISeKGgXAzgtutgJsZc0-kkeqBBscJgMkvy",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/c.zig b/vendor/libghostty-vt/pkg/glslang/c.zig
new file mode 100644
index 00000000..c0010846
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/c.zig
@@ -0,0 +1,4 @@
+pub const c = @cImport({
+ @cInclude("glslang/Include/glslang_c_interface.h");
+ @cInclude("glslang/Public/resource_limits_c.h");
+});
diff --git a/vendor/libghostty-vt/pkg/glslang/init.zig b/vendor/libghostty-vt/pkg/glslang/init.zig
new file mode 100644
index 00000000..a865e9e7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/init.zig
@@ -0,0 +1,9 @@
+const c = @import("c.zig").c;
+
+pub fn init() !void {
+ if (c.glslang_initialize_process() == 0) return error.GlslangInitFailed;
+}
+
+pub fn finalize() void {
+ c.glslang_finalize_process();
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/main.zig b/vendor/libghostty-vt/pkg/glslang/main.zig
new file mode 100644
index 00000000..2743650c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/main.zig
@@ -0,0 +1,15 @@
+const initpkg = @import("init.zig");
+const program = @import("program.zig");
+const shader = @import("shader.zig");
+
+pub const c = @import("c.zig").c;
+pub const testing = @import("test.zig");
+
+pub const init = initpkg.init;
+pub const finalize = initpkg.finalize;
+pub const Program = program.Program;
+pub const Shader = shader.Shader;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/override/glslang/build_info.h b/vendor/libghostty-vt/pkg/glslang/override/glslang/build_info.h
new file mode 100644
index 00000000..c25117ee
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/override/glslang/build_info.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2020 The Khronos Group Inc.
+//
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//
+// Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+//
+// Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+//
+// Neither the name of The Khronos Group Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+// COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+// POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef GLSLANG_BUILD_INFO
+#define GLSLANG_BUILD_INFO
+
+#define GLSLANG_VERSION_MAJOR 13
+#define GLSLANG_VERSION_MINOR 1
+#define GLSLANG_VERSION_PATCH 1
+#define GLSLANG_VERSION_FLAVOR ""
+
+#define GLSLANG_VERSION_GREATER_THAN(major, minor, patch) \
+ ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \
+ ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
+ (GLSLANG_VERSION_PATCH) > (patch)))))
+
+#define GLSLANG_VERSION_GREATER_OR_EQUAL_TO(major, minor, patch) \
+ ((GLSLANG_VERSION_MAJOR) > (major) || ((major) == GLSLANG_VERSION_MAJOR && \
+ ((GLSLANG_VERSION_MINOR) > (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
+ (GLSLANG_VERSION_PATCH >= (patch))))))
+
+#define GLSLANG_VERSION_LESS_THAN(major, minor, patch) \
+ ((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \
+ ((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
+ (GLSLANG_VERSION_PATCH) < (patch)))))
+
+#define GLSLANG_VERSION_LESS_OR_EQUAL_TO(major, minor, patch) \
+ ((GLSLANG_VERSION_MAJOR) < (major) || ((major) == GLSLANG_VERSION_MAJOR && \
+ ((GLSLANG_VERSION_MINOR) < (minor) || ((minor) == GLSLANG_VERSION_MINOR && \
+ (GLSLANG_VERSION_PATCH <= (patch))))))
+
+#endif // GLSLANG_BUILD_INFO
diff --git a/vendor/libghostty-vt/pkg/glslang/program.zig b/vendor/libghostty-vt/pkg/glslang/program.zig
new file mode 100644
index 00000000..4af687cc
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/program.zig
@@ -0,0 +1,60 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const testlib = @import("test.zig");
+const Shader = @import("shader.zig").Shader;
+
+pub const Program = opaque {
+ pub fn create() !*Program {
+ if (c.glslang_program_create()) |ptr| return @ptrCast(ptr);
+ return error.OutOfMemory;
+ }
+
+ pub fn delete(self: *Program) void {
+ c.glslang_program_delete(@ptrCast(self));
+ }
+
+ pub fn addShader(self: *Program, shader: *Shader) void {
+ c.glslang_program_add_shader(@ptrCast(self), @ptrCast(shader));
+ }
+
+ pub fn link(self: *Program, messages: c_int) !void {
+ if (c.glslang_program_link(@ptrCast(self), messages) != 0) return;
+ return error.GlslangFailed;
+ }
+
+ pub fn spirvGenerate(self: *Program, stage: c.glslang_stage_t) void {
+ c.glslang_program_SPIRV_generate(@ptrCast(self), stage);
+ }
+
+ pub fn spirvGetSize(self: *Program) usize {
+ return @intCast(c.glslang_program_SPIRV_get_size(@ptrCast(self)));
+ }
+
+ pub fn spirvGet(self: *Program, buf: []u32) void {
+ c.glslang_program_SPIRV_get(@ptrCast(self), buf.ptr);
+ }
+
+ pub fn spirvGetPtr(self: *Program) ![*]u32 {
+ return @ptrCast(c.glslang_program_SPIRV_get_ptr(@ptrCast(self)));
+ }
+
+ pub fn spirvGetMessages(self: *Program) ![:0]const u8 {
+ const ptr = c.glslang_program_SPIRV_get_messages(@ptrCast(self));
+ return std.mem.sliceTo(ptr, 0);
+ }
+
+ pub fn getInfoLog(self: *Program) ![:0]const u8 {
+ const ptr = c.glslang_program_get_info_log(@ptrCast(self));
+ return std.mem.sliceTo(ptr, 0);
+ }
+
+ pub fn getDebugInfoLog(self: *Program) ![:0]const u8 {
+ const ptr = c.glslang_program_get_info_debug_log(@ptrCast(self));
+ return std.mem.sliceTo(ptr, 0);
+ }
+};
+
+test {
+ var program = try Program.create();
+ defer program.delete();
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/shader.zig b/vendor/libghostty-vt/pkg/glslang/shader.zig
new file mode 100644
index 00000000..36a09f34
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/shader.zig
@@ -0,0 +1,58 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const testlib = @import("test.zig");
+
+pub const Shader = opaque {
+ pub fn create(input: *const c.glslang_input_t) !*Shader {
+ if (c.glslang_shader_create(input)) |ptr| return @ptrCast(ptr);
+ return error.OutOfMemory;
+ }
+
+ pub fn delete(self: *Shader) void {
+ c.glslang_shader_delete(@ptrCast(self));
+ }
+
+ pub fn preprocess(self: *Shader, input: *const c.glslang_input_t) !void {
+ if (c.glslang_shader_preprocess(@ptrCast(self), input) == 0)
+ return error.GlslangFailed;
+ }
+
+ pub fn parse(self: *Shader, input: *const c.glslang_input_t) !void {
+ if (c.glslang_shader_parse(@ptrCast(self), input) == 0)
+ return error.GlslangFailed;
+ }
+
+ pub fn getInfoLog(self: *Shader) ![:0]const u8 {
+ const ptr = c.glslang_shader_get_info_log(@ptrCast(self));
+ return std.mem.sliceTo(ptr, 0);
+ }
+
+ pub fn getDebugInfoLog(self: *Shader) ![:0]const u8 {
+ const ptr = c.glslang_shader_get_info_debug_log(@ptrCast(self));
+ return std.mem.sliceTo(ptr, 0);
+ }
+};
+
+test {
+ const input: c.glslang_input_t = .{
+ .language = c.GLSLANG_SOURCE_GLSL,
+ .stage = c.GLSLANG_STAGE_FRAGMENT,
+ .client = c.GLSLANG_CLIENT_VULKAN,
+ .client_version = c.GLSLANG_TARGET_VULKAN_1_2,
+ .target_language = c.GLSLANG_TARGET_SPV,
+ .target_language_version = c.GLSLANG_TARGET_SPV_1_5,
+ .code = @embedFile("test/simple.frag"),
+ .default_version = 100,
+ .default_profile = c.GLSLANG_NO_PROFILE,
+ .force_default_version_and_profile = 0,
+ .forward_compatible = 0,
+ .messages = c.GLSLANG_MSG_DEFAULT_BIT,
+ .resource = c.glslang_default_resource(),
+ };
+
+ try testlib.ensureInit();
+ const shader = try Shader.create(&input);
+ defer shader.delete();
+ try shader.preprocess(&input);
+ try shader.parse(&input);
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/test.zig b/vendor/libghostty-vt/pkg/glslang/test.zig
new file mode 100644
index 00000000..8cdf98f7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/test.zig
@@ -0,0 +1,10 @@
+const glslang = @import("main.zig");
+
+var initialized: bool = false;
+
+/// Call this function before any other tests in this package to ensure that
+/// the glslang library is initialized.
+pub fn ensureInit() !void {
+ if (initialized) return;
+ try glslang.init();
+}
diff --git a/vendor/libghostty-vt/pkg/glslang/test/simple.frag b/vendor/libghostty-vt/pkg/glslang/test/simple.frag
new file mode 100644
index 00000000..c1cd903c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/glslang/test/simple.frag
@@ -0,0 +1,56 @@
+#version 430 core
+
+layout(binding = 0) uniform Globals {
+ uniform vec3 iResolution;
+ uniform float iTime;
+ uniform float iTimeDelta;
+ uniform float iFrameRate;
+ uniform int iFrame;
+ uniform float iChannelTime[4];
+ uniform vec3 iChannelResolution[4];
+ uniform vec4 iMouse;
+ uniform vec4 iDate;
+ uniform float iSampleRate;
+};
+
+layout(binding = 0) uniform sampler2D iChannel0;
+layout(binding = 1) uniform sampler2D iChannel1;
+layout(binding = 2) uniform sampler2D iChannel2;
+layout(binding = 3) uniform sampler2D iChannel3;
+
+layout(location = 0) in vec4 gl_FragCoord;
+layout(location = 0) out vec4 _fragColor;
+
+#define texture2D texture
+
+void mainImage( out vec4 fragColor, in vec2 fragCoord );
+void main() { mainImage (_fragColor, gl_FragCoord.xy); }
+
+#define t iTime
+
+void mainImage( out vec4 fragColor, in vec2 fragCoord )
+{
+ // Normalized pixel coordinates (from 0 to 1)
+ vec2 uv = ( fragCoord - .5*iResolution.xy) / iResolution.y;
+ vec3 col = vec3(0.);
+ float a = atan(uv.y,uv.x);
+ float r = 0.5*length(uv);
+ float counter = 100.;
+ a = 4.*a+20.*r+50.*cos(r)*cos(.1*t)+abs(a*r);
+ float f = 0.02*abs(cos(a))/(r*r);
+
+
+ vec2 v = vec2(0.);
+ for(float i=0.;i<counter;i++){
+ v = mat2(v,-v.y,v.x) * v + vec2(2.*f+cos(0.5*t*(exp(-0.2* r))),-cos(t*r*r)*cos(0.5*t));
+ if(length(v)>2.){
+ counter = i;
+ break;
+ }
+ }
+
+ col=vec3(min(0.9,1.2*exp(-pow(f,0.45)*counter)));
+
+ fragColor = min(0.9,1.2*exp(-pow(f,0.45)*counter) )
+ * ( 0.7 + 0.3* cos(10.*r - 2.*t -vec4(.7,1.4,2.1,0) ) );
+}
diff --git a/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig b/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig
new file mode 100644
index 00000000..818b48f4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig
@@ -0,0 +1,127 @@
+const std = @import("std");
+
+const version = @import("build.zig.zon").version;
+
+const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+};
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ // Zig API
+ const module = b.addModule("gtk4-layer-shell", .{
+ .root_source_file = b.path("src/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+ // Needs the gtk.h header
+ module.linkSystemLibrary("gtk4", dynamic_link_opts);
+
+ if (b.systemIntegrationOption("gtk4-layer-shell", .{})) {
+ module.linkSystemLibrary("gtk4-layer-shell-0", dynamic_link_opts);
+ } else {
+ _ = try buildLib(b, module, .{
+ .target = target,
+ .optimize = optimize,
+ });
+ }
+}
+
+fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
+ const lib_version = try std.SemanticVersion.parse(version);
+ const target = options.target;
+ const optimize = options.optimize;
+
+ // Shared library
+ const lib = b.addLibrary(.{
+ .name = "gtk4-layer-shell",
+ .linkage = .dynamic,
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ b.installArtifact(lib);
+
+ // We need to call both lazy dependencies to tell Zig we need both
+ const upstream_ = b.lazyDependency("gtk4_layer_shell", .{});
+ const wayland_protocols_ = b.lazyDependency("wayland_protocols", .{});
+ const upstream = upstream_ orelse return lib;
+ const wayland_protocols = wayland_protocols_ orelse return lib;
+
+ lib.linkLibC();
+ lib.addIncludePath(upstream.path("include"));
+ lib.addIncludePath(upstream.path("src"));
+ module.addIncludePath(upstream.path("include"));
+
+ // GTK
+ lib.linkSystemLibrary2("gtk4", dynamic_link_opts);
+
+ // Wayland headers and source files
+ {
+ const protocols = [_]struct { []const u8, std.Build.LazyPath }{
+ .{
+ "wlr-layer-shell-unstable-v1",
+ upstream.path("protocol/wlr-layer-shell-unstable-v1.xml"),
+ },
+ .{
+ "xdg-shell",
+ wayland_protocols.path("stable/xdg-shell/xdg-shell.xml"),
+ },
+ // Even though we don't use session lock, we still need its headers
+ .{
+ "ext-session-lock-v1",
+ wayland_protocols.path("staging/ext-session-lock/ext-session-lock-v1.xml"),
+ },
+ };
+
+ const wf = b.addWriteFiles();
+ for (protocols) |protocol| {
+ const name, const xml = protocol;
+
+ const header_scanner = b.addSystemCommand(&.{ "wayland-scanner", "client-header" });
+ header_scanner.addFileArg(xml);
+ _ = wf.addCopyFile(
+ header_scanner.addOutputFileArg(name),
+ b.fmt("{s}-client.h", .{name}),
+ );
+
+ const source_scanner = b.addSystemCommand(&.{ "wayland-scanner", "private-code" });
+ source_scanner.addFileArg(xml);
+ const source = source_scanner.addOutputFileArg(b.fmt("{s}.c", .{name}));
+ lib.addCSourceFile(.{ .file = source });
+ }
+ lib.addIncludePath(wf.getDirectory());
+ }
+
+ lib.installHeadersDirectory(
+ upstream.path("include"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+
+ // Certain files relating to session lock were removed as we don't use them
+ const srcs: []const []const u8 = &.{
+ "gtk4-layer-shell.c",
+ "layer-surface.c",
+ "libwayland-shim.c",
+ "registry.c",
+ "stolen-from-libwayland.c",
+ "stubbed-surface.c",
+ "xdg-surface-server.c",
+ };
+ lib.addCSourceFiles(.{
+ .root = upstream.path("src"),
+ .files = srcs,
+ .flags = &.{
+ b.fmt("-DGTK_LAYER_SHELL_MAJOR={}", .{lib_version.major}),
+ b.fmt("-DGTK_LAYER_SHELL_MINOR={}", .{lib_version.minor}),
+ b.fmt("-DGTK_LAYER_SHELL_MICRO={}", .{lib_version.patch}),
+ },
+ });
+
+ return lib;
+}
diff --git a/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig.zon b/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig.zon
new file mode 100644
index 00000000..9329e374
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/gtk4-layer-shell/build.zig.zon
@@ -0,0 +1,18 @@
+.{
+ .name = .gtk4_layer_shell,
+ .version = "1.1.0",
+ .fingerprint = 0x4b96f9483c6feeb1,
+ .paths = .{""},
+ .dependencies = .{
+ .gtk4_layer_shell = .{
+ .url = "https://deps.files.ghostty.org/gtk4-layer-shell-1.1.0.tar.gz",
+ .hash = "N-V-__8AALiNBAA-_0gprYr92CjrMj1I5bqNu0TSJOnjFNSr",
+ .lazy = true,
+ },
+ .wayland_protocols = .{
+ .url = "https://deps.files.ghostty.org/wayland-protocols-258d8f88f2c8c25a830c6316f87d23ce1a0f12d9.tar.gz",
+ .hash = "N-V-__8AAKw-DAAaV8bOAAGqA0-oD7o-HNIlPFYKRXSPT03S",
+ .lazy = true,
+ },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/gtk4-layer-shell/src/main.zig b/vendor/libghostty-vt/pkg/gtk4-layer-shell/src/main.zig
new file mode 100644
index 00000000..a1531323
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/gtk4-layer-shell/src/main.zig
@@ -0,0 +1,71 @@
+const std = @import("std");
+
+const c = @cImport({
+ @cInclude("gtk4-layer-shell.h");
+});
+const gdk = @import("gdk");
+const gtk = @import("gtk");
+
+pub const ShellLayer = enum(c_uint) {
+ background = c.GTK_LAYER_SHELL_LAYER_BACKGROUND,
+ bottom = c.GTK_LAYER_SHELL_LAYER_BOTTOM,
+ top = c.GTK_LAYER_SHELL_LAYER_TOP,
+ overlay = c.GTK_LAYER_SHELL_LAYER_OVERLAY,
+};
+
+pub const ShellEdge = enum(c_uint) {
+ left = c.GTK_LAYER_SHELL_EDGE_LEFT,
+ right = c.GTK_LAYER_SHELL_EDGE_RIGHT,
+ top = c.GTK_LAYER_SHELL_EDGE_TOP,
+ bottom = c.GTK_LAYER_SHELL_EDGE_BOTTOM,
+};
+
+pub const KeyboardMode = enum(c_uint) {
+ none = c.GTK_LAYER_SHELL_KEYBOARD_MODE_NONE,
+ exclusive = c.GTK_LAYER_SHELL_KEYBOARD_MODE_EXCLUSIVE,
+ on_demand = c.GTK_LAYER_SHELL_KEYBOARD_MODE_ON_DEMAND,
+};
+
+pub fn isSupported() bool {
+ return c.gtk_layer_is_supported() != 0;
+}
+
+pub fn getProtocolVersion() c_uint {
+ return c.gtk_layer_get_protocol_version();
+}
+
+pub fn getLibraryVersion() std.SemanticVersion {
+ return .{
+ .major = c.gtk_layer_get_major_version(),
+ .minor = c.gtk_layer_get_minor_version(),
+ .patch = c.gtk_layer_get_micro_version(),
+ };
+}
+
+pub fn initForWindow(window: *gtk.Window) void {
+ c.gtk_layer_init_for_window(@ptrCast(window));
+}
+
+pub fn setLayer(window: *gtk.Window, layer: ShellLayer) void {
+ c.gtk_layer_set_layer(@ptrCast(window), @intFromEnum(layer));
+}
+
+pub fn setAnchor(window: *gtk.Window, edge: ShellEdge, anchor_to_edge: bool) void {
+ c.gtk_layer_set_anchor(@ptrCast(window), @intFromEnum(edge), @intFromBool(anchor_to_edge));
+}
+
+pub fn setMargin(window: *gtk.Window, edge: ShellEdge, margin_size: c_int) void {
+ c.gtk_layer_set_margin(@ptrCast(window), @intFromEnum(edge), margin_size);
+}
+
+pub fn setKeyboardMode(window: *gtk.Window, mode: KeyboardMode) void {
+ c.gtk_layer_set_keyboard_mode(@ptrCast(window), @intFromEnum(mode));
+}
+
+pub fn setMonitor(window: *gtk.Window, monitor: ?*gdk.Monitor) void {
+ c.gtk_layer_set_monitor(@ptrCast(window), @ptrCast(monitor));
+}
+
+pub fn setNamespace(window: *gtk.Window, name: [:0]const u8) void {
+ c.gtk_layer_set_namespace(@ptrCast(window), name.ptr);
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/blob.zig b/vendor/libghostty-vt/pkg/harfbuzz/blob.zig
new file mode 100644
index 00000000..9472e4c7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/blob.zig
@@ -0,0 +1,126 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Error = @import("errors.zig").Error;
+
+/// Data type holding the memory modes available to client programs.
+///
+/// Regarding these various memory-modes:
+///
+/// - In no case shall the HarfBuzz client modify memory that is passed to
+/// HarfBuzz in a blob. If there is any such possibility,
+/// HB_MEMORY_MODE_DUPLICATE should be used such that HarfBuzz makes a
+/// copy immediately,
+///
+/// - Use HB_MEMORY_MODE_READONLY otherwise, unless you really really really
+/// know what you are doing,
+///
+/// - HB_MEMORY_MODE_WRITABLE is appropriate if you really made a copy of
+/// data solely for the purpose of passing to HarfBuzz and doing that
+/// just once (no reuse!),
+///
+/// - If the font is mmap()ed, it's okay to use
+/// HB_MEMORY_READONLY_MAY_MAKE_WRITABLE , however, using that mode
+/// correctly is very tricky. Use HB_MEMORY_MODE_READONLY instead.
+pub const MemoryMode = enum(u2) {
+ /// HarfBuzz immediately makes a copy of the data.
+ duplicate = c.HB_MEMORY_MODE_DUPLICATE,
+
+ /// HarfBuzz client will never modify the data, and HarfBuzz will never
+ /// modify the data.
+ readonly = c.HB_MEMORY_MODE_READONLY,
+
+ /// HarfBuzz client made a copy of the data solely for HarfBuzz, so
+ /// HarfBuzz may modify the data.
+ writable = c.HB_MEMORY_MODE_WRITABLE,
+
+ /// See above
+ readonly_may_make_writable = c.HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
+};
+
+/// Blobs wrap a chunk of binary data to handle lifecycle management of data
+/// while it is passed between client and HarfBuzz. Blobs are primarily
+/// used to create font faces, but also to access font face tables, as well as
+/// pass around other binary data.
+pub const Blob = struct {
+ handle: *c.hb_blob_t,
+
+ /// Creates a new "blob" object wrapping data . The mode parameter is used
+ /// to negotiate ownership and lifecycle of data .
+ ///
+ /// Note that this function returns a freshly-allocated empty blob even
+ /// if length is zero. This is in contrast to hb_blob_create(), which
+ /// returns the singleton empty blob (as returned by hb_blob_get_empty())
+ /// if length is zero.
+ pub fn create(data: []const u8, mode: MemoryMode) Error!Blob {
+ const handle = c.hb_blob_create_or_fail(
+ data.ptr,
+ @intCast(data.len),
+ @intFromEnum(mode),
+ null,
+ null,
+ ) orelse return Error.HarfbuzzFailed;
+
+ return Blob{ .handle = handle };
+ }
+
+ /// Decreases the reference count on blob , and if it reaches zero,
+ /// destroys blob , freeing all memory, possibly calling the
+ /// destroy-callback the blob was created for if it has not been
+ /// called already.
+ pub fn destroy(self: *Blob) void {
+ c.hb_blob_destroy(self.handle);
+ }
+
+ /// Attaches a user-data key/data pair to the specified blob.
+ pub fn setUserData(
+ self: Blob,
+ comptime T: type,
+ key: ?*anyopaque,
+ ptr: ?*T,
+ comptime destroycb: ?*const fn (?*T) callconv(.c) void,
+ replace: bool,
+ ) bool {
+ const Callback = struct {
+ pub fn callback(data: ?*anyopaque) callconv(.c) void {
+ @call(.{ .modifier = .always_inline }, destroycb, .{
+ @as(?*T, @ptrCast(@alignCast(data))),
+ });
+ }
+ };
+
+ return c.hb_blob_set_user_data(
+ self.handle,
+ @ptrCast(key),
+ ptr,
+ if (destroycb != null) Callback.callback else null,
+ if (replace) 1 else 0,
+ ) > 0;
+ }
+
+ /// Fetches the user data associated with the specified key, attached to
+ /// the specified font-functions structure.
+ pub fn getUserData(
+ self: Blob,
+ comptime T: type,
+ key: ?*anyopaque,
+ ) ?*T {
+ const opt = c.hb_blob_get_user_data(self.handle, @ptrCast(key));
+ if (opt) |ptr|
+ return @ptrCast(@alignCast(ptr))
+ else
+ return null;
+ }
+};
+
+test {
+ const testing = std.testing;
+
+ const data = "hello";
+ var blob = try Blob.create(data, .readonly);
+ defer blob.destroy();
+
+ var userdata: u8 = 127;
+ var key: u8 = 0;
+ try testing.expect(blob.setUserData(u8, &key, &userdata, null, false));
+ try testing.expect(blob.getUserData(u8, &key).?.* == 127);
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig
new file mode 100644
index 00000000..b97c1bef
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/buffer.zig
@@ -0,0 +1,373 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const common = @import("common.zig");
+const Error = @import("errors.zig").Error;
+const Direction = common.Direction;
+const Script = common.Script;
+const Language = common.Language;
+
+/// Buffers serve a dual role in HarfBuzz; before shaping, they hold the
+/// input characters that are passed to hb_shape(), and after shaping they
+/// hold the output glyphs.
+pub const Buffer = struct {
+ handle: *c.hb_buffer_t,
+
+ /// Creates a new hb_buffer_t with all properties to defaults.
+ pub fn create() Error!Buffer {
+ const handle = c.hb_buffer_create() orelse return Error.HarfbuzzFailed;
+ return Buffer{ .handle = handle };
+ }
+
+ /// Deallocate the buffer . Decreases the reference count on buffer by one.
+ /// If the result is zero, then buffer and all associated resources are
+ /// freed. See hb_buffer_reference().
+ pub fn destroy(self: *Buffer) void {
+ c.hb_buffer_destroy(self.handle);
+ }
+
+ /// Resets the buffer to its initial status, as if it was just newly
+ /// created with hb_buffer_create().
+ pub fn reset(self: Buffer) void {
+ c.hb_buffer_reset(self.handle);
+ }
+
+ /// Returns the number of items in the buffer.
+ pub fn getLength(self: Buffer) u32 {
+ return c.hb_buffer_get_length(self.handle);
+ }
+
+ /// Sets the type of buffer contents. Buffers are either empty, contain
+ /// characters (before shaping), or contain glyphs (the result of shaping).
+ pub fn setContentType(self: Buffer, ct: ContentType) void {
+ c.hb_buffer_set_content_type(self.handle, @intFromEnum(ct));
+ }
+
+ /// Fetches the type of buffer contents. Buffers are either empty, contain
+ /// characters (before shaping), or contain glyphs (the result of shaping).
+ pub fn getContentType(self: Buffer) ContentType {
+ return @enumFromInt(c.hb_buffer_get_content_type(self.handle));
+ }
+
+ /// Appends a character with the Unicode value of codepoint to buffer,
+ /// and gives it the initial cluster value of cluster . Clusters can be
+ /// any thing the client wants, they are usually used to refer to the
+ /// index of the character in the input text stream and are output in
+ /// hb_glyph_info_t.cluster field.
+ ///
+ /// This function does not check the validity of codepoint, it is up to
+ /// the caller to ensure it is a valid Unicode code point.
+ pub fn add(self: Buffer, cp: u32, cluster: u32) void {
+ c.hb_buffer_add(self.handle, cp, cluster);
+ }
+
+ /// Appends characters from text array to buffer . The item_offset is the
+ /// position of the first character from text that will be appended, and
+ /// item_length is the number of character. When shaping part of a larger
+ /// text (e.g. a run of text from a paragraph), instead of passing just
+ /// the substring corresponding to the run, it is preferable to pass the
+ /// whole paragraph and specify the run start and length as item_offset and
+ /// item_length , respectively, to give HarfBuzz the full context to be
+ /// able, for example, to do cross-run Arabic shaping or properly handle
+ /// combining marks at stat of run.
+ ///
+ /// This function does not check the validity of text , it is up to the
+ /// caller to ensure it contains a valid Unicode code points.
+ pub fn addCodepoints(self: Buffer, text: []const u32) void {
+ c.hb_buffer_add_codepoints(
+ self.handle,
+ text.ptr,
+ @intCast(text.len),
+ 0,
+ @intCast(text.len),
+ );
+ }
+
+ /// See hb_buffer_add_codepoints().
+ ///
+ /// Replaces invalid UTF-32 characters with the buffer replacement code
+ /// point, see hb_buffer_set_replacement_codepoint().
+ pub fn addUTF32(self: Buffer, text: []const u32) void {
+ c.hb_buffer_add_utf32(
+ self.handle,
+ text.ptr,
+ @intCast(text.len),
+ 0,
+ @intCast(text.len),
+ );
+ }
+
+ /// See hb_buffer_add_codepoints().
+ ///
+ /// Replaces invalid UTF-16 characters with the buffer replacement code
+ /// point, see hb_buffer_set_replacement_codepoint().
+ pub fn addUTF16(self: Buffer, text: []const u16) void {
+ c.hb_buffer_add_utf16(
+ self.handle,
+ text.ptr,
+ @intCast(text.len),
+ 0,
+ @intCast(text.len),
+ );
+ }
+
+ /// See hb_buffer_add_codepoints().
+ ///
+ /// Replaces invalid UTF-8 characters with the buffer replacement code
+ /// point, see hb_buffer_set_replacement_codepoint().
+ pub fn addUTF8(self: Buffer, text: []const u8) void {
+ c.hb_buffer_add_utf8(
+ self.handle,
+ text.ptr,
+ @intCast(text.len),
+ 0,
+ @intCast(text.len),
+ );
+ }
+
+ /// Similar to hb_buffer_add_codepoints(), but allows only access to first
+ /// 256 Unicode code points that can fit in 8-bit strings.
+ pub fn addLatin1(self: Buffer, text: []const u8) void {
+ c.hb_buffer_add_latin1(
+ self.handle,
+ text.ptr,
+ @intCast(text.len),
+ 0,
+ @intCast(text.len),
+ );
+ }
+
+ /// Set the text flow direction of the buffer. No shaping can happen
+ /// without setting buffer direction, and it controls the visual direction
+ /// for the output glyphs; for RTL direction the glyphs will be reversed.
+ /// Many layout features depend on the proper setting of the direction,
+ /// for example, reversing RTL text before shaping, then shaping with LTR
+ /// direction is not the same as keeping the text in logical order and
+ /// shaping with RTL direction.
+ pub fn setDirection(self: Buffer, dir: Direction) void {
+ c.hb_buffer_set_direction(self.handle, @intFromEnum(dir));
+ }
+
+ /// See hb_buffer_set_direction()
+ pub fn getDirection(self: Buffer) Direction {
+ return @enumFromInt(c.hb_buffer_get_direction(self.handle));
+ }
+
+ /// Sets the script of buffer to script.
+ ///
+ /// Script is crucial for choosing the proper shaping behaviour for
+ /// scripts that require it (e.g. Arabic) and the which OpenType features
+ /// defined in the font to be applied.
+ ///
+ /// You can pass one of the predefined hb_script_t values, or use
+ /// hb_script_from_string() or hb_script_from_iso15924_tag() to get the
+ /// corresponding script from an ISO 15924 script tag.
+ pub fn setScript(self: Buffer, script: Script) void {
+ c.hb_buffer_set_script(self.handle, @intFromEnum(script));
+ }
+
+ /// See hb_buffer_set_script()
+ pub fn getScript(self: Buffer) Script {
+ return @enumFromInt(c.hb_buffer_get_script(self.handle));
+ }
+
+ /// Sets the language of buffer to language .
+ ///
+ /// Languages are crucial for selecting which OpenType feature to apply to
+ /// the buffer which can result in applying language-specific behaviour.
+ /// Languages are orthogonal to the scripts, and though they are related,
+ /// they are different concepts and should not be confused with each other.
+ ///
+ /// Use hb_language_from_string() to convert from BCP 47 language tags to
+ /// hb_language_t.
+ pub fn setLanguage(self: Buffer, language: Language) void {
+ c.hb_buffer_set_language(self.handle, language.handle);
+ }
+
+ /// See hb_buffer_set_language()
+ pub fn getLanguage(self: Buffer) Language {
+ return Language{ .handle = c.hb_buffer_get_language(self.handle) };
+ }
+
+ /// Returns buffer glyph information array. Returned pointer is valid as
+ /// long as buffer contents are not modified.
+ pub fn getGlyphInfos(self: Buffer) []GlyphInfo {
+ var length: u32 = 0;
+ const ptr: [*c]GlyphInfo = @ptrCast(c.hb_buffer_get_glyph_infos(self.handle, &length));
+ return ptr[0..length];
+ }
+
+ /// Returns buffer glyph position array. Returned pointer is valid as
+ /// long as buffer contents are not modified.
+ ///
+ /// If buffer did not have positions before, the positions will be
+ /// initialized to zeros, unless this function is called from within a
+ /// buffer message callback (see hb_buffer_set_message_func()), in which
+ /// case NULL is returned.
+ pub fn getGlyphPositions(self: Buffer) ?[]GlyphPosition {
+ var length: u32 = 0;
+
+ if (c.hb_buffer_get_glyph_positions(self.handle, &length)) |positions| {
+ const ptr: [*]GlyphPosition = @ptrCast(positions);
+ return ptr[0..length];
+ }
+
+ return null;
+ }
+
+ /// Sets unset buffer segment properties based on buffer Unicode contents.
+ /// If buffer is not empty, it must have content type
+ /// HB_BUFFER_CONTENT_TYPE_UNICODE.
+ ///
+ /// If buffer script is not set (ie. is HB_SCRIPT_INVALID), it will be set
+ /// to the Unicode script of the first character in the buffer that has a
+ /// script other than HB_SCRIPT_COMMON, HB_SCRIPT_INHERITED, and
+ /// HB_SCRIPT_UNKNOWN.
+ ///
+ /// Next, if buffer direction is not set (ie. is HB_DIRECTION_INVALID), it
+ /// will be set to the natural horizontal direction of the buffer script as
+ /// returned by hb_script_get_horizontal_direction(). If
+ /// hb_script_get_horizontal_direction() returns HB_DIRECTION_INVALID,
+ /// then HB_DIRECTION_LTR is used.
+ ///
+ /// Finally, if buffer language is not set (ie. is HB_LANGUAGE_INVALID), it
+ /// will be set to the process's default language as returned by
+ /// hb_language_get_default(). This may change in the future by taking
+ /// buffer script into consideration when choosing a language. Note that
+ /// hb_language_get_default() is NOT threadsafe the first time it is
+ /// called. See documentation for that function for details.
+ pub fn guessSegmentProperties(self: Buffer) void {
+ c.hb_buffer_guess_segment_properties(self.handle);
+ }
+
+ /// Sets the cluster level of a buffer. The `ClusterLevel` dictates one
+ /// aspect of how HarfBuzz will treat non-base characters during shaping.
+ pub fn setClusterLevel(self: Buffer, level: ClusterLevel) void {
+ c.hb_buffer_set_cluster_level(self.handle, @intFromEnum(level));
+ }
+};
+
+/// The type of hb_buffer_t contents.
+pub const ContentType = enum(u2) {
+ /// Initial value for new buffer.
+ invalid = c.HB_BUFFER_CONTENT_TYPE_INVALID,
+
+ /// The buffer contains input characters (before shaping).
+ unicode = c.HB_BUFFER_CONTENT_TYPE_UNICODE,
+
+ /// The buffer contains output glyphs (after shaping).
+ glyphs = c.HB_BUFFER_CONTENT_TYPE_GLYPHS,
+};
+
+/// Data type for holding HarfBuzz's clustering behavior options. The cluster
+/// level dictates one aspect of how HarfBuzz will treat non-base characters
+/// during shaping.
+pub const ClusterLevel = enum(u2) {
+ /// In `monotone_graphemes`, non-base characters are merged into the
+ /// cluster of the base character that precedes them. There is also cluster
+ /// merging every time the clusters will otherwise become non-monotone.
+ /// This is the default cluster level.
+ monotone_graphemes = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_GRAPHEMES,
+
+ /// In `monotone_characters`, non-base characters are initially assigned
+ /// their own cluster values, which are not merged into preceding base
+ /// clusters. This allows HarfBuzz to perform additional operations like
+ /// reorder sequences of adjacent marks. The output is still monotone, but
+ /// the cluster values are more granular.
+ monotone_characters = c.HB_BUFFER_CLUSTER_LEVEL_MONOTONE_CHARACTERS,
+
+ /// In `characters`, non-base characters are assigned their own cluster
+ /// values, which are not merged into preceding base clusters. Moreover,
+ /// the cluster values are not merged into monotone order. This is the most
+ /// granular cluster level, and it is useful for clients that need to know
+ /// the exact cluster values of each character, but is harder to use for
+ /// clients, since clusters might appear in any order.
+ characters = c.HB_BUFFER_CLUSTER_LEVEL_CHARACTERS,
+
+ /// In `graphemes`, non-base characters are merged into the cluster of the
+ /// base character that precedes them. This is similar to the Unicode
+ /// Grapheme Cluster algorithm, but it is not exactly the same. The output
+ /// is not forced to be monotone. This is useful for clients that want to
+ /// use HarfBuzz as a cheap implementation of the Unicode Grapheme Cluster
+ /// algorithm.
+ graphemes = c.HB_BUFFER_CLUSTER_LEVEL_GRAPHEMES,
+};
+
+/// The hb_glyph_info_t is the structure that holds information about the
+/// glyphs and their relation to input text.
+pub const GlyphInfo = extern struct {
+ /// either a Unicode code point (before shaping) or a glyph index (after shaping).
+ codepoint: u32,
+ _mask: u32,
+
+ /// the index of the character in the original text that corresponds to
+ /// this hb_glyph_info_t, or whatever the client passes to hb_buffer_add().
+ /// More than one hb_glyph_info_t can have the same cluster value, if they
+ /// resulted from the same character (e.g. one to many glyph substitution),
+ /// and when more than one character gets merged in the same glyph (e.g.
+ /// many to one glyph substitution) the hb_glyph_info_t will have the
+ /// smallest cluster value of them. By default some characters are merged
+ /// into the same cluster (e.g. combining marks have the same cluster as
+ /// their bases) even if they are separate glyphs, hb_buffer_set_cluster_level()
+ /// allow selecting more fine-grained cluster handling.
+ cluster: u32,
+ _var1: u32,
+ _var2: u32,
+};
+
+/// The hb_glyph_position_t is the structure that holds the positions of the
+/// glyph in both horizontal and vertical directions. All positions in
+/// hb_glyph_position_t are relative to the current point.
+pub const GlyphPosition = extern struct {
+ /// how much the line advances after drawing this glyph when setting text
+ /// in horizontal direction.
+ x_advance: i32,
+
+ /// how much the line advances after drawing this glyph when setting text
+ /// in vertical direction.
+ y_advance: i32,
+
+ /// how much the glyph moves on the X-axis before drawing it, this should
+ /// not affect how much the line advances.
+ x_offset: i32,
+
+ /// how much the glyph moves on the Y-axis before drawing it, this should
+ /// not affect how much the line advances.
+ y_offset: i32,
+
+ _var: u32,
+};
+
+test "create" {
+ const testing = std.testing;
+
+ var buffer = try Buffer.create();
+ defer buffer.destroy();
+ buffer.reset();
+
+ // Content type
+ buffer.setContentType(.unicode);
+ try testing.expectEqual(ContentType.unicode, buffer.getContentType());
+
+ // Try add functions
+ buffer.add('🥹', 27);
+ var utf32 = [_]u32{ 'A', 'B', 'C' };
+ var utf16 = [_]u16{ 'A', 'B', 'C' };
+ var utf8 = [_]u8{ 'A', 'B', 'C' };
+ buffer.addCodepoints(&utf32);
+ buffer.addUTF32(&utf32);
+ buffer.addUTF16(&utf16);
+ buffer.addUTF8(&utf8);
+ buffer.addLatin1(&utf8);
+
+ // Guess properties first
+ buffer.guessSegmentProperties();
+
+ // Try to set properties
+ buffer.setDirection(.ltr);
+ try testing.expectEqual(Direction.ltr, buffer.getDirection());
+
+ buffer.setScript(.arabic);
+ try testing.expectEqual(Script.arabic, buffer.getScript());
+
+ buffer.setLanguage(Language.fromString("en"));
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/build.zig b/vendor/libghostty-vt/pkg/harfbuzz/build.zig
new file mode 100644
index 00000000..6d8f3be7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/build.zig
@@ -0,0 +1,185 @@
+const std = @import("std");
+const apple_sdk = @import("apple_sdk");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const coretext_enabled = b.option(bool, "enable-coretext", "Build coretext") orelse false;
+ const freetype_enabled = b.option(bool, "enable-freetype", "Build freetype") orelse true;
+
+ const freetype = b.dependency("freetype", .{
+ .target = target,
+ .optimize = optimize,
+ .@"enable-libpng" = true,
+ });
+ const macos = b.dependency("macos", .{ .target = target, .optimize = optimize });
+
+ const module = harfbuzz: {
+ const module = b.addModule("harfbuzz", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ .imports = &.{
+ .{ .name = "freetype", .module = freetype.module("freetype") },
+ .{ .name = "macos", .module = macos.module("macos") },
+ },
+ });
+
+ const options = b.addOptions();
+ options.addOption(bool, "coretext", coretext_enabled);
+ options.addOption(bool, "freetype", freetype_enabled);
+ module.addOptions("build_options", options);
+ break :harfbuzz module;
+ };
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+
+ {
+ var it = module.import_table.iterator();
+ while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
+ if (b.systemIntegrationOption("freetype", .{})) {
+ test_exe.linkSystemLibrary2("freetype2", dynamic_link_opts);
+ } else {
+ test_exe.linkLibrary(freetype.artifact("freetype"));
+ }
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+ }
+
+ if (b.systemIntegrationOption("harfbuzz", .{})) {
+ module.linkSystemLibrary("harfbuzz", dynamic_link_opts);
+ test_exe.linkSystemLibrary2("harfbuzz", dynamic_link_opts);
+ } else {
+ const lib = try buildLib(b, module, .{
+ .target = target,
+ .optimize = optimize,
+
+ .coretext_enabled = coretext_enabled,
+ .freetype_enabled = freetype_enabled,
+
+ .dynamic_link_opts = dynamic_link_opts,
+ });
+
+ test_exe.linkLibrary(lib);
+ }
+}
+
+fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
+ const target = options.target;
+ const optimize = options.optimize;
+
+ const coretext_enabled = options.coretext_enabled;
+ const freetype_enabled = options.freetype_enabled;
+
+ const freetype = b.dependency("freetype", .{
+ .target = target,
+ .optimize = optimize,
+ .@"enable-libpng" = true,
+ });
+
+ const lib = b.addLibrary(.{
+ .name = "harfbuzz",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+
+ if (target.result.os.tag.isDarwin()) {
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ const dynamic_link_opts = options.dynamic_link_opts;
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_STDBOOL_H",
+ });
+ if (target.result.os.tag != .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_UNISTD_H",
+ "-DHAVE_SYS_MMAN_H",
+ "-DHAVE_PTHREAD=1",
+ });
+ }
+
+ // Freetype
+ _ = b.systemIntegrationOption("freetype", .{}); // So it shows up in help
+ if (freetype_enabled) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_FREETYPE=1",
+
+ // Let's just assume a new freetype
+ "-DHAVE_FT_GET_VAR_BLEND_COORDINATES=1",
+ "-DHAVE_FT_SET_VAR_BLEND_COORDINATES=1",
+ "-DHAVE_FT_DONE_MM_VAR=1",
+ "-DHAVE_FT_GET_TRANSFORM=1",
+ });
+
+ if (b.systemIntegrationOption("freetype", .{})) {
+ lib.linkSystemLibrary2("freetype2", dynamic_link_opts);
+ module.linkSystemLibrary("freetype2", dynamic_link_opts);
+ } else {
+ lib.linkLibrary(freetype.artifact("freetype"));
+
+ if (freetype.builder.lazyDependency(
+ "freetype",
+ .{},
+ )) |freetype_dep| {
+ module.addIncludePath(freetype_dep.path("include"));
+ }
+ }
+ }
+
+ if (coretext_enabled) {
+ try flags.appendSlice(b.allocator, &.{"-DHAVE_CORETEXT=1"});
+ lib.linkFramework("CoreText");
+ module.linkFramework("CoreText", .{});
+ }
+
+ if (b.lazyDependency("harfbuzz", .{})) |upstream| {
+ lib.addIncludePath(upstream.path("src"));
+ module.addIncludePath(upstream.path("src"));
+ lib.addCSourceFile(.{
+ .file = upstream.path("src/harfbuzz.cc"),
+ .flags = flags.items,
+ });
+ lib.installHeadersDirectory(
+ upstream.path("src"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ return lib;
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon b/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon
new file mode 100644
index 00000000..b7d155e9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/build.zig.zon
@@ -0,0 +1,18 @@
+.{
+ .name = .harfbuzz,
+ .version = "11.0.0",
+ .fingerprint = 0xbd60917cd18865d8,
+ .paths = .{""},
+ .dependencies = .{
+ // harfbuzz/harfbuzz
+ .harfbuzz = .{
+ .url = "https://deps.files.ghostty.org/harfbuzz-11.0.0.tar.xz",
+ .hash = "N-V-__8AAG02ugUcWec-Ndp-i7JTsJ0dgF8nnJRUInkGLG7G",
+ .lazy = true,
+ },
+
+ .freetype = .{ .path = "../freetype" },
+ .macos = .{ .path = "../macos" },
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/c.zig b/vendor/libghostty-vt/pkg/harfbuzz/c.zig
new file mode 100644
index 00000000..49e87dce
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/c.zig
@@ -0,0 +1,8 @@
+const builtin = @import("builtin");
+const build_options = @import("build_options");
+
+pub const c = @cImport({
+ @cInclude("hb.h");
+ if (build_options.freetype) @cInclude("hb-ft.h");
+ if (build_options.coretext) @cInclude("hb-coretext.h");
+});
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/common.zig b/vendor/libghostty-vt/pkg/harfbuzz/common.zig
new file mode 100644
index 00000000..840ff586
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/common.zig
@@ -0,0 +1,247 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+/// The direction of a text segment or buffer.
+///
+/// A segment can also be tested for horizontal or vertical orientation
+/// (irrespective of specific direction) with HB_DIRECTION_IS_HORIZONTAL()
+/// or HB_DIRECTION_IS_VERTICAL().
+pub const Direction = enum(u3) {
+ invalid = c.HB_DIRECTION_INVALID,
+ ltr = c.HB_DIRECTION_LTR,
+ rtl = c.HB_DIRECTION_RTL,
+ ttb = c.HB_DIRECTION_TTB,
+ bit = c.HB_DIRECTION_BTT,
+};
+
+/// Data type for scripts. Each hb_script_t's value is an hb_tag_t
+/// corresponding to the four-letter values defined by ISO 15924.
+///
+/// See also the Script (sc) property of the Unicode Character Database.
+pub const Script = enum(u31) {
+ common = c.HB_SCRIPT_COMMON,
+ inherited = c.HB_SCRIPT_INHERITED,
+ unknown = c.HB_SCRIPT_UNKNOWN,
+ arabic = c.HB_SCRIPT_ARABIC,
+ armenian = c.HB_SCRIPT_ARMENIAN,
+ bengali = c.HB_SCRIPT_BENGALI,
+ cyrillic = c.HB_SCRIPT_CYRILLIC,
+ devanagari = c.HB_SCRIPT_DEVANAGARI,
+ georgian = c.HB_SCRIPT_GEORGIAN,
+ greek = c.HB_SCRIPT_GREEK,
+ gujarati = c.HB_SCRIPT_GUJARATI,
+ gurmukhi = c.HB_SCRIPT_GURMUKHI,
+ hangul = c.HB_SCRIPT_HANGUL,
+ han = c.HB_SCRIPT_HAN,
+ hebrew = c.HB_SCRIPT_HEBREW,
+ hiragana = c.HB_SCRIPT_HIRAGANA,
+ kannada = c.HB_SCRIPT_KANNADA,
+ katakana = c.HB_SCRIPT_KATAKANA,
+ lao = c.HB_SCRIPT_LAO,
+ latin = c.HB_SCRIPT_LATIN,
+ malayalam = c.HB_SCRIPT_MALAYALAM,
+ oriya = c.HB_SCRIPT_ORIYA,
+ tamil = c.HB_SCRIPT_TAMIL,
+ telugu = c.HB_SCRIPT_TELUGU,
+ thai = c.HB_SCRIPT_THAI,
+ tibetan = c.HB_SCRIPT_TIBETAN,
+ bopomofo = c.HB_SCRIPT_BOPOMOFO,
+ braille = c.HB_SCRIPT_BRAILLE,
+ canadian_syllabics = c.HB_SCRIPT_CANADIAN_SYLLABICS,
+ cherokee = c.HB_SCRIPT_CHEROKEE,
+ ethiopic = c.HB_SCRIPT_ETHIOPIC,
+ khmer = c.HB_SCRIPT_KHMER,
+ mongolian = c.HB_SCRIPT_MONGOLIAN,
+ myanmar = c.HB_SCRIPT_MYANMAR,
+ ogham = c.HB_SCRIPT_OGHAM,
+ runic = c.HB_SCRIPT_RUNIC,
+ sinhala = c.HB_SCRIPT_SINHALA,
+ syriac = c.HB_SCRIPT_SYRIAC,
+ thaana = c.HB_SCRIPT_THAANA,
+ yi = c.HB_SCRIPT_YI,
+ deseret = c.HB_SCRIPT_DESERET,
+ gothic = c.HB_SCRIPT_GOTHIC,
+ old_italic = c.HB_SCRIPT_OLD_ITALIC,
+ buhid = c.HB_SCRIPT_BUHID,
+ hanunoo = c.HB_SCRIPT_HANUNOO,
+ tagalog = c.HB_SCRIPT_TAGALOG,
+ tagbanwa = c.HB_SCRIPT_TAGBANWA,
+ cypriot = c.HB_SCRIPT_CYPRIOT,
+ limbu = c.HB_SCRIPT_LIMBU,
+ linear_b = c.HB_SCRIPT_LINEAR_B,
+ osmanya = c.HB_SCRIPT_OSMANYA,
+ shavian = c.HB_SCRIPT_SHAVIAN,
+ tai_le = c.HB_SCRIPT_TAI_LE,
+ ugaritic = c.HB_SCRIPT_UGARITIC,
+ buginese = c.HB_SCRIPT_BUGINESE,
+ coptic = c.HB_SCRIPT_COPTIC,
+ glagolitic = c.HB_SCRIPT_GLAGOLITIC,
+ kharoshthi = c.HB_SCRIPT_KHAROSHTHI,
+ new_tai_lue = c.HB_SCRIPT_NEW_TAI_LUE,
+ old_persian = c.HB_SCRIPT_OLD_PERSIAN,
+ syloti_nagri = c.HB_SCRIPT_SYLOTI_NAGRI,
+ tifinagh = c.HB_SCRIPT_TIFINAGH,
+ balinese = c.HB_SCRIPT_BALINESE,
+ cuneiform = c.HB_SCRIPT_CUNEIFORM,
+ nko = c.HB_SCRIPT_NKO,
+ phags_pa = c.HB_SCRIPT_PHAGS_PA,
+ phoenician = c.HB_SCRIPT_PHOENICIAN,
+ carian = c.HB_SCRIPT_CARIAN,
+ cham = c.HB_SCRIPT_CHAM,
+ kayah_li = c.HB_SCRIPT_KAYAH_LI,
+ lepcha = c.HB_SCRIPT_LEPCHA,
+ lycian = c.HB_SCRIPT_LYCIAN,
+ lydian = c.HB_SCRIPT_LYDIAN,
+ ol_chiki = c.HB_SCRIPT_OL_CHIKI,
+ rejang = c.HB_SCRIPT_REJANG,
+ saurashtra = c.HB_SCRIPT_SAURASHTRA,
+ sundanese = c.HB_SCRIPT_SUNDANESE,
+ vai = c.HB_SCRIPT_VAI,
+ avestan = c.HB_SCRIPT_AVESTAN,
+ bamum = c.HB_SCRIPT_BAMUM,
+ egyptian_hieroglyphs = c.HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
+ imperial_aramaic = c.HB_SCRIPT_IMPERIAL_ARAMAIC,
+ inscriptional_pahlavi = c.HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
+ inscriptional_parthian = c.HB_SCRIPT_INSCRIPTIONAL_PARTHIAN,
+ javanese = c.HB_SCRIPT_JAVANESE,
+ kaithi = c.HB_SCRIPT_KAITHI,
+ lisu = c.HB_SCRIPT_LISU,
+ meetei_mayek = c.HB_SCRIPT_MEETEI_MAYEK,
+ old_south_arabian = c.HB_SCRIPT_OLD_SOUTH_ARABIAN,
+ old_turkic = c.HB_SCRIPT_OLD_TURKIC,
+ samaritan = c.HB_SCRIPT_SAMARITAN,
+ tai_tham = c.HB_SCRIPT_TAI_THAM,
+ tai_viet = c.HB_SCRIPT_TAI_VIET,
+ batak = c.HB_SCRIPT_BATAK,
+ brahmi = c.HB_SCRIPT_BRAHMI,
+ mandaic = c.HB_SCRIPT_MANDAIC,
+ chakma = c.HB_SCRIPT_CHAKMA,
+ meroitic_cursive = c.HB_SCRIPT_MEROITIC_CURSIVE,
+ meroitic_hieroglyphs = c.HB_SCRIPT_MEROITIC_HIEROGLYPHS,
+ miao = c.HB_SCRIPT_MIAO,
+ sharada = c.HB_SCRIPT_SHARADA,
+ sora_sompeng = c.HB_SCRIPT_SORA_SOMPENG,
+ takri = c.HB_SCRIPT_TAKRI,
+ bassa_vah = c.HB_SCRIPT_BASSA_VAH,
+ caucasian_albanian = c.HB_SCRIPT_CAUCASIAN_ALBANIAN,
+ duployan = c.HB_SCRIPT_DUPLOYAN,
+ elbasan = c.HB_SCRIPT_ELBASAN,
+ grantha = c.HB_SCRIPT_GRANTHA,
+ khojki = c.HB_SCRIPT_KHOJKI,
+ khudawadi = c.HB_SCRIPT_KHUDAWADI,
+ linear_a = c.HB_SCRIPT_LINEAR_A,
+ mahajani = c.HB_SCRIPT_MAHAJANI,
+ manichaean = c.HB_SCRIPT_MANICHAEAN,
+ mende_kikakui = c.HB_SCRIPT_MENDE_KIKAKUI,
+ modi = c.HB_SCRIPT_MODI,
+ mro = c.HB_SCRIPT_MRO,
+ nabataean = c.HB_SCRIPT_NABATAEAN,
+ old_north_arabian = c.HB_SCRIPT_OLD_NORTH_ARABIAN,
+ old_permic = c.HB_SCRIPT_OLD_PERMIC,
+ pahawh_hmong = c.HB_SCRIPT_PAHAWH_HMONG,
+ palmyrene = c.HB_SCRIPT_PALMYRENE,
+ pau_cin_hau = c.HB_SCRIPT_PAU_CIN_HAU,
+ psalter_pahlavi = c.HB_SCRIPT_PSALTER_PAHLAVI,
+ siddham = c.HB_SCRIPT_SIDDHAM,
+ tirhuta = c.HB_SCRIPT_TIRHUTA,
+ warang_citi = c.HB_SCRIPT_WARANG_CITI,
+ ahom = c.HB_SCRIPT_AHOM,
+ anatolian_hieroglyphs = c.HB_SCRIPT_ANATOLIAN_HIEROGLYPHS,
+ hatran = c.HB_SCRIPT_HATRAN,
+ multani = c.HB_SCRIPT_MULTANI,
+ old_hungarian = c.HB_SCRIPT_OLD_HUNGARIAN,
+ signwriting = c.HB_SCRIPT_SIGNWRITING,
+ adlam = c.HB_SCRIPT_ADLAM,
+ bhaiksuki = c.HB_SCRIPT_BHAIKSUKI,
+ marchen = c.HB_SCRIPT_MARCHEN,
+ osage = c.HB_SCRIPT_OSAGE,
+ tangut = c.HB_SCRIPT_TANGUT,
+ newa = c.HB_SCRIPT_NEWA,
+ masaram_gondi = c.HB_SCRIPT_MASARAM_GONDI,
+ nushu = c.HB_SCRIPT_NUSHU,
+ soyombo = c.HB_SCRIPT_SOYOMBO,
+ zanabazar_square = c.HB_SCRIPT_ZANABAZAR_SQUARE,
+ dogra = c.HB_SCRIPT_DOGRA,
+ gunjala_gondi = c.HB_SCRIPT_GUNJALA_GONDI,
+ hanifi_rohingya = c.HB_SCRIPT_HANIFI_ROHINGYA,
+ makasar = c.HB_SCRIPT_MAKASAR,
+ medefaidrin = c.HB_SCRIPT_MEDEFAIDRIN,
+ old_sogdian = c.HB_SCRIPT_OLD_SOGDIAN,
+ sogdian = c.HB_SCRIPT_SOGDIAN,
+ elymaic = c.HB_SCRIPT_ELYMAIC,
+ nandinagari = c.HB_SCRIPT_NANDINAGARI,
+ nyiakeng_puachue_hmong = c.HB_SCRIPT_NYIAKENG_PUACHUE_HMONG,
+ wancho = c.HB_SCRIPT_WANCHO,
+ chorasmian = c.HB_SCRIPT_CHORASMIAN,
+ dives_akuru = c.HB_SCRIPT_DIVES_AKURU,
+ khitan_small_script = c.HB_SCRIPT_KHITAN_SMALL_SCRIPT,
+ yezidi = c.HB_SCRIPT_YEZIDI,
+ cypro_minoan = c.HB_SCRIPT_CYPRO_MINOAN,
+ old_uyghur = c.HB_SCRIPT_OLD_UYGHUR,
+ tangsa = c.HB_SCRIPT_TANGSA,
+ toto = c.HB_SCRIPT_TOTO,
+ vithkuqi = c.HB_SCRIPT_VITHKUQI,
+ math = c.HB_SCRIPT_MATH,
+ invalid = c.HB_SCRIPT_INVALID,
+};
+
+/// Data type for languages. Each hb_language_t corresponds to a BCP 47
+/// language tag.
+pub const Language = struct {
+ handle: c.hb_language_t,
+
+ /// Converts str representing a BCP 47 language tag to the corresponding
+ /// hb_language_t.
+ pub fn fromString(str: []const u8) Language {
+ return .{
+ .handle = c.hb_language_from_string(str.ptr, @intCast(str.len)),
+ };
+ }
+
+ /// Converts an hb_language_t to a string.
+ pub fn toString(self: Language) [:0]const u8 {
+ return std.mem.span(@as(
+ [*:0]const u8,
+ @ptrCast(c.hb_language_to_string(self.handle)),
+ ));
+ }
+
+ /// Fetch the default language from current locale.
+ pub fn getDefault() Language {
+ return .{ .handle = c.hb_language_get_default() };
+ }
+};
+
+/// The hb_feature_t is the structure that holds information about requested
+/// feature application. The feature will be applied with the given value to
+/// all glyphs which are in clusters between start (inclusive) and end
+/// (exclusive). Setting start to HB_FEATURE_GLOBAL_START and end to
+/// HB_FEATURE_GLOBAL_END specifies that the feature always applies to the
+/// entire buffer.
+pub const Feature = extern struct {
+ tag: c.hb_tag_t,
+ value: u32,
+ start: c_uint,
+ end: c_uint,
+
+ pub fn fromString(str: []const u8) ?Feature {
+ var f: c.hb_feature_t = undefined;
+ return if (c.hb_feature_from_string(
+ str.ptr,
+ @intCast(str.len),
+ &f,
+ ) > 0)
+ @bitCast(f)
+ else
+ null;
+ }
+
+ pub fn toString(self: *Feature, buf: []u8) void {
+ c.hb_feature_to_string(self, buf.ptr, @intCast(buf.len));
+ }
+};
+
+test "feature from string" {
+ const testing = std.testing;
+ try testing.expect(Feature.fromString("dlig") != null);
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig b/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig
new file mode 100644
index 00000000..73042624
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/coretext.zig
@@ -0,0 +1,30 @@
+const macos = @import("macos");
+const std = @import("std");
+const c = @import("c.zig").c;
+const Face = @import("face.zig").Face;
+const Font = @import("font.zig").Font;
+const Error = @import("errors.zig").Error;
+
+// Use custom extern functions so that the proper CoreText structs are used
+// without a ptrcast.
+extern fn hb_coretext_font_create(ct_face: *macos.text.Font) ?*c.hb_font_t;
+
+/// Creates an hb_font_t font object from the specified CTFontRef.
+pub fn createFont(face: *macos.text.Font) Error!Font {
+ const handle = hb_coretext_font_create(face) orelse return Error.HarfbuzzFailed;
+ return Font{ .handle = handle };
+}
+
+test {
+ if (!@hasDecl(c, "hb_coretext_font_create")) return error.SkipZigTest;
+
+ const name = try macos.foundation.String.createWithBytes("Monaco", .utf8, false);
+ defer name.release();
+ const desc = try macos.text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+ const font = try macos.text.Font.createWithFontDescriptor(desc, 12);
+ defer font.release();
+
+ var hb_font = try createFont(font);
+ defer hb_font.destroy();
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/errors.zig b/vendor/libghostty-vt/pkg/harfbuzz/errors.zig
new file mode 100644
index 00000000..44a4d055
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/errors.zig
@@ -0,0 +1,5 @@
+pub const Error = error{
+ /// Not very descriptive but harfbuzz doesn't actually have error
+ /// codes so the best we can do!
+ HarfbuzzFailed,
+};
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/face.zig b/vendor/libghostty-vt/pkg/harfbuzz/face.zig
new file mode 100644
index 00000000..5ffad5d3
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/face.zig
@@ -0,0 +1,17 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+/// A font face is an object that represents a single face from within a font family.
+///
+/// More precisely, a font face represents a single face in a binary font file.
+/// Font faces are typically built from a binary blob and a face index.
+/// Font faces are used to create fonts.
+pub const Face = struct {
+ handle: *c.hb_face_t,
+
+ /// Decreases the reference count on a face object. When the reference
+ /// count reaches zero, the face is destroyed, freeing all memory.
+ pub fn destroy(self: *Face) void {
+ c.hb_face_destroy(self.handle);
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/font.zig b/vendor/libghostty-vt/pkg/harfbuzz/font.zig
new file mode 100644
index 00000000..13544fb9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/font.zig
@@ -0,0 +1,28 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Face = @import("face.zig").Face;
+const Error = @import("errors.zig").Error;
+
+pub const Font = struct {
+ handle: *c.hb_font_t,
+
+ /// Constructs a new font object from the specified face.
+ pub fn create(face: Face) Error!Font {
+ const handle = c.hb_font_create(face.handle) orelse return Error.HarfbuzzFailed;
+ return Font{ .handle = handle };
+ }
+
+ /// Decreases the reference count on the given font object. When the
+ /// reference count reaches zero, the font is destroyed, freeing all memory.
+ pub fn destroy(self: *Font) void {
+ c.hb_font_destroy(self.handle);
+ }
+
+ pub fn setScale(self: *Font, x: u32, y: u32) void {
+ c.hb_font_set_scale(
+ self.handle,
+ @intCast(x),
+ @intCast(y),
+ );
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig b/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig
new file mode 100644
index 00000000..d90d3f83
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/freetype.zig
@@ -0,0 +1,78 @@
+const freetype = @import("freetype");
+const std = @import("std");
+const c = @import("c.zig").c;
+const Face = @import("face.zig").Face;
+const Font = @import("font.zig").Font;
+const Error = @import("errors.zig").Error;
+
+// Use custom extern functions so that the proper freetype structs are used
+// without a ptrcast. These are only needed when interacting with freetype
+// C structs.
+extern fn hb_ft_face_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_face_t;
+extern fn hb_ft_font_create_referenced(ft_face: freetype.c.FT_Face) ?*c.hb_font_t;
+extern fn hb_ft_font_get_face(font: ?*c.hb_font_t) freetype.c.FT_Face;
+
+/// Creates an hb_face_t face object from the specified FT_Face.
+///
+/// This is the preferred variant of the hb_ft_face_create* function
+/// family, because it calls FT_Reference_Face() on ft_face , ensuring
+/// that ft_face remains alive as long as the resulting hb_face_t face
+/// object remains alive. Also calls FT_Done_Face() when the hb_face_t
+/// face object is destroyed.
+///
+/// Use this version unless you know you have good reasons not to.
+pub fn createFace(face: freetype.c.FT_Face) Error!Face {
+ const handle = hb_ft_face_create_referenced(face) orelse return Error.HarfbuzzFailed;
+ return Face{ .handle = handle };
+}
+
+/// Creates an hb_font_t font object from the specified FT_Face.
+pub fn createFont(face: freetype.c.FT_Face) Error!Font {
+ const handle = hb_ft_font_create_referenced(face) orelse return Error.HarfbuzzFailed;
+ return Font{ .handle = handle };
+}
+
+/// Configures the font-functions structure of the specified hb_font_t font
+/// object to use FreeType font functions.
+///
+/// In particular, you can use this function to configure an existing
+/// hb_face_t face object for use with FreeType font functions even if that
+/// hb_face_t face object was initially created with hb_face_create(), and
+/// therefore was not initially configured to use FreeType font functions.
+///
+/// An hb_face_t face object created with hb_ft_face_create() is preconfigured
+/// for FreeType font functions and does not require this function to be used.
+pub fn setFontFuncs(font: Font) void {
+ c.hb_ft_font_set_funcs(font.handle);
+}
+
+test {
+ if (!@hasDecl(c, "hb_ft_font_create_referenced")) return error.SkipZigTest;
+
+ const testing = std.testing;
+ const testFont = freetype.testing.font_regular;
+ const ftc = freetype.c;
+ const ftok = ftc.FT_Err_Ok;
+
+ var ft_lib: ftc.FT_Library = undefined;
+ if (ftc.FT_Init_FreeType(&ft_lib) != ftok)
+ return error.FreeTypeInitFailed;
+ defer _ = ftc.FT_Done_FreeType(ft_lib);
+
+ var ft_face: ftc.FT_Face = undefined;
+ try testing.expect(ftc.FT_New_Memory_Face(
+ ft_lib,
+ testFont,
+ @intCast(testFont.len),
+ 0,
+ &ft_face,
+ ) == ftok);
+ defer _ = ftc.FT_Done_Face(ft_face);
+
+ var face = try createFace(ft_face);
+ defer face.destroy();
+
+ var font = try createFont(ft_face);
+ defer font.destroy();
+ setFontFuncs(font);
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/main.zig b/vendor/libghostty-vt/pkg/harfbuzz/main.zig
new file mode 100644
index 00000000..08a4f9c2
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/main.zig
@@ -0,0 +1,31 @@
+const blob = @import("blob.zig");
+const buffer = @import("buffer.zig");
+const common = @import("common.zig");
+const errors = @import("errors.zig");
+const face = @import("face.zig");
+const font = @import("font.zig");
+const shapepkg = @import("shape.zig");
+const versionpkg = @import("version.zig");
+
+pub const c = @import("c.zig").c;
+pub const freetype = @import("freetype.zig");
+pub const coretext = @import("coretext.zig");
+pub const MemoryMode = blob.MemoryMode;
+pub const Blob = blob.Blob;
+pub const Buffer = buffer.Buffer;
+pub const GlyphPosition = buffer.GlyphPosition;
+pub const Direction = common.Direction;
+pub const Script = common.Script;
+pub const Language = common.Language;
+pub const Feature = common.Feature;
+pub const Face = face.Face;
+pub const Font = font.Font;
+pub const shape = shapepkg.shape;
+pub const Version = versionpkg.Version;
+pub const version = versionpkg.version;
+pub const versionAtLeast = versionpkg.versionAtLeast;
+pub const versionString = versionpkg.versionString;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/shape.zig b/vendor/libghostty-vt/pkg/harfbuzz/shape.zig
new file mode 100644
index 00000000..b49020c3
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/shape.zig
@@ -0,0 +1,27 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const Font = @import("font.zig").Font;
+const Buffer = @import("buffer.zig").Buffer;
+const Feature = @import("common.zig").Feature;
+
+/// Shapes buffer using font turning its Unicode characters content to
+/// positioned glyphs. If features is not NULL, it will be used to control
+/// the features applied during shaping. If two features have the same tag
+/// but overlapping ranges the value of the feature with the higher index
+/// takes precedence.
+pub fn shape(font: Font, buf: Buffer, features: ?[]const Feature) void {
+ const hb_feats: [*c]const c.hb_feature_t = feats: {
+ if (features) |fs| {
+ if (fs.len > 0) break :feats @ptrCast(fs.ptr);
+ }
+
+ break :feats null;
+ };
+
+ c.hb_shape(
+ font.handle,
+ buf.handle,
+ hb_feats,
+ if (features) |f| @intCast(f.len) else 0,
+ );
+}
diff --git a/vendor/libghostty-vt/pkg/harfbuzz/version.zig b/vendor/libghostty-vt/pkg/harfbuzz/version.zig
new file mode 100644
index 00000000..dbe25b52
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/harfbuzz/version.zig
@@ -0,0 +1,47 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+pub const Version = struct {
+ major: u32,
+ minor: u32,
+ micro: u32,
+};
+
+/// Returns library version as three integer components.
+pub fn version() Version {
+ var major: c_uint = 0;
+ var minor: c_uint = 0;
+ var micro: c_uint = 0;
+ c.hb_version(&major, &minor, &micro);
+ return .{ .major = major, .minor = minor, .micro = micro };
+}
+
+/// Tests the library version against a minimum value, as three integer components.
+pub fn versionAtLeast(vsn: Version) bool {
+ return c.hb_version_atleast(
+ vsn.major,
+ vsn.minor,
+ vsn.micro,
+ ) > 0;
+}
+
+/// Returns library version as a string with three components.
+pub fn versionString() [:0]const u8 {
+ const res = c.hb_version_string();
+ return std.mem.sliceTo(res, 0);
+}
+
+test {
+ const testing = std.testing;
+
+ // Should be able to get the version
+ const vsn = version();
+ try testing.expect(vsn.major > 0);
+
+ // Should be at least version 1
+ try testing.expect(versionAtLeast(.{
+ .major = 1,
+ .minor = 0,
+ .micro = 0,
+ }));
+}
diff --git a/vendor/libghostty-vt/pkg/highway/bridge.cpp b/vendor/libghostty-vt/pkg/highway/bridge.cpp
new file mode 100644
index 00000000..1e86c493
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/highway/bridge.cpp
@@ -0,0 +1,9 @@
+#include <hwy/targets.h>
+#include <stdint.h>
+
+extern "C" {
+
+int64_t hwy_supported_targets() {
+ return HWY_SUPPORTED_TARGETS;
+}
+}
diff --git a/vendor/libghostty-vt/pkg/highway/build.zig b/vendor/libghostty-vt/pkg/highway/build.zig
new file mode 100644
index 00000000..49656b93
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/highway/build.zig
@@ -0,0 +1,141 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const upstream_ = b.lazyDependency("highway", .{});
+
+ const module = b.addModule("highway", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const lib = b.addLibrary(.{
+ .name = "highway",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+ if (upstream_) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ module.addIncludePath(upstream.path(""));
+ }
+
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (target.result.abi.isAndroid()) {
+ const android_ndk = @import("android_ndk");
+ try android_ndk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ // Avoid changing binaries based on the current time and date.
+ "-Wno-builtin-macro-redefined",
+ "-D__DATE__=\"redacted\"",
+ "-D__TIMESTAMP__=\"redacted\"",
+ "-D__TIME__=\"redacted\"",
+
+ // Optimizations
+ "-fmerge-all-constants",
+
+ // Warnings
+ "-Wall",
+ "-Wextra",
+
+ // These are not included in Wall nor Wextra:
+ "-Wconversion",
+ "-Wsign-conversion",
+ "-Wvla",
+ "-Wnon-virtual-dtor",
+
+ "-Wfloat-overflow-conversion",
+ "-Wfloat-zero-conversion",
+ "-Wfor-loop-analysis",
+ "-Wgnu-redeclared-enum",
+ "-Winfinite-recursion",
+ "-Wself-assign",
+ "-Wstring-conversion",
+ "-Wtautological-overlap-compare",
+ "-Wthread-safety-analysis",
+ "-Wundefined-func-template",
+
+ "-fno-cxx-exceptions",
+ "-fno-slp-vectorize",
+ "-fno-vectorize",
+
+ // Fixes linker issues for release builds missing ubsanitizer symbols
+ "-fno-sanitize=undefined",
+ "-fno-sanitize-trap=undefined",
+ });
+
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ if (target.result.os.tag != .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-fmath-errno",
+ "-fno-exceptions",
+ });
+ }
+
+ lib.addCSourceFiles(.{ .flags = flags.items, .files = &.{"bridge.cpp"} });
+ if (upstream_) |upstream| {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ "hwy/abort.cc",
+ "hwy/aligned_allocator.cc",
+ "hwy/nanobenchmark.cc",
+ "hwy/per_target.cc",
+ "hwy/print.cc",
+ "hwy/targets.cc",
+ "hwy/timer.cc",
+ },
+ });
+ lib.installHeadersDirectory(
+ upstream.path("hwy"),
+ "hwy",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ {
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ test_exe.linkLibrary(lib);
+
+ var it = module.import_table.iterator();
+ while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/highway/build.zig.zon b/vendor/libghostty-vt/pkg/highway/build.zig.zon
new file mode 100644
index 00000000..4870d1db
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/highway/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .highway,
+ .version = "1.2.0",
+ .fingerprint = 0xdbcf1a7425023274,
+ .paths = .{""},
+ .dependencies = .{
+ // google/highway
+ .highway = .{
+ .url = "https://deps.files.ghostty.org/highway-66486a10623fa0d72fe91260f96c892e41aceb06.tar.gz",
+ .hash = "N-V-__8AAGmZhABbsPJLfbqrh6JTHsXhY6qCaLAQyx25e0XE",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .android_ndk = .{ .path = "../android-ndk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/highway/main.zig b/vendor/libghostty-vt/pkg/highway/main.zig
new file mode 100644
index 00000000..95ba6cda
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/highway/main.zig
@@ -0,0 +1,57 @@
+extern "c" fn hwy_supported_targets() i64;
+
+pub const Targets = packed struct(i64) {
+ // x86_64
+ _reserved: u4 = 0,
+ avx3_spr: bool = false,
+ _reserved_5: u1 = 0,
+ avx3_zen4: bool = false,
+ avx3_dl: bool = false,
+ avx3: bool = false,
+ avx2: bool = false,
+ _reserved_10: u1 = 0,
+ sse4: bool = false,
+ ssse3: bool = false,
+ _reserved_13: u1 = 0, // SSE3 reserved
+ sse2: bool = false,
+ _reserved_15_23: u9 = 0,
+
+ // aarch64
+ sve2_128: bool = false,
+ sve_256: bool = false,
+ sve2: bool = false,
+ sve: bool = false,
+ neon: bool = false,
+ neon_without_aes: bool = false,
+ _reserved_30_36: u6 = 0,
+
+ // risc-v
+ rvv: bool = false,
+ _reserved_38_46: u9 = 0,
+
+ // IBM Power
+ ppc10: bool = false,
+ ppc9: bool = false,
+ ppc8: bool = false,
+ z15: bool = false,
+ z14: bool = false,
+ _reserved_52_57: u6 = 0,
+
+ // WebAssembly
+ wasm_emu256: bool = false,
+ wasm: bool = false,
+ _reserved_60_61: u2 = 0,
+
+ // Emulation
+ emu128: bool = false,
+ scalar: bool = false,
+ _reserved_63: u1 = 0,
+};
+
+pub fn supported_targets() Targets {
+ return @bitCast(hwy_supported_targets());
+}
+
+test {
+ _ = supported_targets();
+}
diff --git a/vendor/libghostty-vt/pkg/libintl/build.zig b/vendor/libghostty-vt/pkg/libintl/build.zig
new file mode 100644
index 00000000..32221e5a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libintl/build.zig
@@ -0,0 +1,104 @@
+//! Provides libintl for macOS.
+//!
+//! IMPORTANT: This is only for macOS. We could support other platforms
+//! if/when we need to but generally Linux provides libintl in libc.
+//! Windows we'll have to figure out when we get there.
+//!
+//! Since this is only for macOS, there's a lot of hardcoded stuff
+//! here that assumes macOS. For example, I generated the config.h
+//! on my own machine (a Mac) and then copied it here. This isn't
+//! ideal since we should do the same detection that gettext's configure
+//! script does, but its quite a bit of work to do that.
+//!
+//! UPGRADING: If you need to upgrade gettext, then the only thing to
+//! really watch out for is the xlocale.h include we added manually
+//! at the end of config.h. The comment there notes why. When we upgrade
+//! we should audit our config.h and make sure we add that back (if we
+//! have to).
+
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_CONFIG_H",
+ "-DLOCALEDIR=\"\"",
+ });
+
+ {
+ const lib = b.addLibrary(.{
+ .name = "intl",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ lib.addIncludePath(b.path(""));
+
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (b.lazyDependency("gettext", .{})) |upstream| {
+ lib.addIncludePath(upstream.path("gettext-runtime/intl"));
+ lib.addIncludePath(upstream.path("gettext-runtime/intl/gnulib-lib"));
+ lib.addCSourceFiles(.{
+ .root = upstream.path("gettext-runtime/intl"),
+ .files = srcs,
+ .flags = flags.items,
+ });
+ }
+
+ lib.installHeader(b.path("libintl.h"), "libintl.h");
+ b.installArtifact(lib);
+ }
+}
+
+const srcs: []const []const u8 = &.{
+ "bindtextdom.c",
+ "dcgettext.c",
+ "dcigettext.c",
+ "dcngettext.c",
+ "dgettext.c",
+ "dngettext.c",
+ "explodename.c",
+ "finddomain.c",
+ "gettext.c",
+ "hash-string.c",
+ "intl-compat.c",
+ "l10nflist.c",
+ "langprefs.c",
+ "loadmsgcat.c",
+ "localealias.c",
+ "log.c",
+ "ngettext.c",
+ "plural-exp.c",
+ "plural.c",
+ "setlocale.c",
+ "textdomain.c",
+ "version.c",
+ "compat.c",
+
+ // There's probably a better way to detect that we need these, but
+ // these are hardcoded for now for macOS.
+ "gnulib-lib/getlocalename_l-unsafe.c",
+ "gnulib-lib/localename.c",
+ "gnulib-lib/localename-environ.c",
+ "gnulib-lib/localename-unsafe.c",
+ "gnulib-lib/setlocale-lock.c",
+ "gnulib-lib/setlocale_null.c",
+ "gnulib-lib/setlocale_null-unlocked.c",
+
+ // Not needed for macOS, but we might need them for other platforms.
+ // If we expand this to support other platforms, we should uncomment
+ // these.
+ // "osdep.c",
+ // "printf.c",
+};
diff --git a/vendor/libghostty-vt/pkg/libintl/build.zig.zon b/vendor/libghostty-vt/pkg/libintl/build.zig.zon
new file mode 100644
index 00000000..a32e2009
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libintl/build.zig.zon
@@ -0,0 +1,15 @@
+.{
+ .name = .libintl,
+ .version = "0.24.0",
+ .fingerprint = 0x16434c723ba7278a,
+ .paths = .{""},
+ .dependencies = .{
+ .gettext = .{
+ .url = "https://deps.files.ghostty.org/gettext-0.24.tar.gz",
+ .hash = "N-V-__8AADcZkgn4cMhTUpIz6mShCKyqqB-NBtf_S2bHaTC-",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/libintl/config.h b/vendor/libghostty-vt/pkg/libintl/config.h
new file mode 100644
index 00000000..93d4e024
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libintl/config.h
@@ -0,0 +1,2370 @@
+/* config.h. Generated from config.h.in by configure. */
+/* config.h.in. Generated from configure.ac by autoheader. */
+
+/* Witness that <config.h> has been included. */
+#define _GL_CONFIG_H_INCLUDED 1
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define if no multithread safety and no multithreading is desired. */
+/* #undef AVOID_ANY_THREADS */
+
+/* Define to the number of bits in type 'ptrdiff_t'. */
+/* #undef BITSIZEOF_PTRDIFF_T */
+
+/* Define to the number of bits in type 'sig_atomic_t'. */
+/* #undef BITSIZEOF_SIG_ATOMIC_T */
+
+/* Define to the number of bits in type 'size_t'. */
+/* #undef BITSIZEOF_SIZE_T */
+
+/* Define to the number of bits in type 'wchar_t'. */
+/* #undef BITSIZEOF_WCHAR_T */
+
+/* Define to the number of bits in type 'wint_t'. */
+/* #undef BITSIZEOF_WINT_T */
+
+/* Define if you wish *printf() functions that have a safe handling of
+ non-IEEE-754 'long double' values. */
+#define CHECK_PRINTF_SAFE 1
+
+/* Define to 1 if using 'alloca.c'. */
+/* #undef C_ALLOCA */
+
+/* Define as the bit index in the word where to find bit 0 of the exponent of
+ 'double'. */
+#define DBL_EXPBIT0_BIT 20
+
+/* Define as the word index where to find the exponent of 'double'. */
+#define DBL_EXPBIT0_WORD 1
+
+/* Define as the bit index in the word where to find the sign of 'double'. */
+/* #undef DBL_SIGNBIT_BIT */
+
+/* Define as the word index where to find the sign of 'double'. */
+/* #undef DBL_SIGNBIT_WORD */
+
+/* Define to 1 if translation of program messages to the user's native
+ language is requested. */
+#define ENABLE_NLS 1
+
+/* Define to nothing if C supports flexible array members, and to 1 if it does
+ not. That way, with a declaration like 'struct s { int n; short
+ d[FLEXIBLE_ARRAY_MEMBER]; };', the struct hack can be used with pre-C99
+ compilers. Use 'FLEXSIZEOF (struct s, d, N * sizeof (short))' to calculate
+ the size in bytes of such a struct containing an N-element array. */
+#define FLEXIBLE_ARRAY_MEMBER /**/
+
+/* Define as the bit index in the word where to find bit 0 of the exponent of
+ 'float'. */
+#define FLT_EXPBIT0_BIT 23
+
+/* Define as the word index where to find the exponent of 'float'. */
+#define FLT_EXPBIT0_WORD 0
+
+/* Define as the bit index in the word where to find the sign of 'float'. */
+/* #undef FLT_SIGNBIT_BIT */
+
+/* Define as the word index where to find the sign of 'float'. */
+/* #undef FLT_SIGNBIT_WORD */
+
+/* Define to a C preprocessor expression that evaluates to 1 or 0, depending
+ whether the gnulib module fscanf shall be considered present. */
+#define GNULIB_FSCANF 1
+
+/* Define to a C preprocessor expression that evaluates to 1 or 0, depending
+ whether the gnulib module lock shall be considered present. */
+#define GNULIB_LOCK 1
+
+/* Define to 1 if printf and friends should be labeled with attribute
+ "__gnu_printf__" instead of "__printf__" */
+/* #undef GNULIB_PRINTF_ATTRIBUTE_FLAVOR_GNU */
+
+/* Define to a C preprocessor expression that evaluates to 1 or 0, depending
+ whether the gnulib module scanf shall be considered present. */
+#define GNULIB_SCANF 1
+
+/* Define to 1 when the gnulib module fgetc should be tested. */
+#define GNULIB_TEST_FGETC 1
+
+/* Define to 1 when the gnulib module fgets should be tested. */
+#define GNULIB_TEST_FGETS 1
+
+/* Define to 1 when the gnulib module fprintf should be tested. */
+#define GNULIB_TEST_FPRINTF 1
+
+/* Define to 1 when the gnulib module fputc should be tested. */
+#define GNULIB_TEST_FPUTC 1
+
+/* Define to 1 when the gnulib module fputs should be tested. */
+#define GNULIB_TEST_FPUTS 1
+
+/* Define to 1 when the gnulib module fread should be tested. */
+#define GNULIB_TEST_FREAD 1
+
+/* Define to 1 when the gnulib module free-posix should be tested. */
+#define GNULIB_TEST_FREE_POSIX 1
+
+/* Define to 1 when the gnulib module frexp should be tested. */
+#define GNULIB_TEST_FREXP 1
+
+/* Define to 1 when the gnulib module frexpl should be tested. */
+#define GNULIB_TEST_FREXPL 1
+
+/* Define to 1 when the gnulib module fscanf should be tested. */
+#define GNULIB_TEST_FSCANF 1
+
+/* Define to 1 when the gnulib module fwrite should be tested. */
+#define GNULIB_TEST_FWRITE 1
+
+/* Define to 1 when the gnulib module getc should be tested. */
+#define GNULIB_TEST_GETC 1
+
+/* Define to 1 when the gnulib module getchar should be tested. */
+#define GNULIB_TEST_GETCHAR 1
+
+/* Define to 1 when the gnulib module getcwd should be tested. */
+#define GNULIB_TEST_GETCWD 1
+
+/* Define to 1 when the gnulib module getlocalename_l-unsafe should be tested.
+ */
+#define GNULIB_TEST_GETLOCALENAME_L_UNSAFE 1
+
+/* Define to 1 when the gnulib module localename-environ should be tested. */
+#define GNULIB_TEST_LOCALENAME_ENVIRON 1
+
+/* Define to 1 when the gnulib module localename-unsafe should be tested. */
+#define GNULIB_TEST_LOCALENAME_UNSAFE 1
+
+/* Define to 1 when the gnulib module mbrtowc should be tested. */
+#define GNULIB_TEST_MBRTOWC 1
+
+/* Define to 1 when the gnulib module mbsinit should be tested. */
+#define GNULIB_TEST_MBSINIT 1
+
+/* Define to 1 when the gnulib module mbszero should be tested. */
+#define GNULIB_TEST_MBSZERO 1
+
+/* Define to 1 when the gnulib module memchr should be tested. */
+#define GNULIB_TEST_MEMCHR 1
+
+/* Define to 1 when the gnulib module printf should be tested. */
+#define GNULIB_TEST_PRINTF 1
+
+/* Define to 1 when the gnulib module pthread-once should be tested. */
+#define GNULIB_TEST_PTHREAD_ONCE 1
+
+/* Define to 1 when the gnulib module putc should be tested. */
+#define GNULIB_TEST_PUTC 1
+
+/* Define to 1 when the gnulib module putchar should be tested. */
+#define GNULIB_TEST_PUTCHAR 1
+
+/* Define to 1 when the gnulib module puts should be tested. */
+#define GNULIB_TEST_PUTS 1
+
+/* Define to 1 when the gnulib module scanf should be tested. */
+#define GNULIB_TEST_SCANF 1
+
+/* Define to 1 when the gnulib module setlocale_null should be tested. */
+#define GNULIB_TEST_SETLOCALE_NULL 1
+
+/* Define to 1 when the gnulib module signbit should be tested. */
+#define GNULIB_TEST_SIGNBIT 1
+
+/* Define to 1 when the gnulib module tsearch should be tested. */
+#define GNULIB_TEST_TSEARCH 1
+
+/* Define to 1 when the gnulib module vfprintf should be tested. */
+#define GNULIB_TEST_VFPRINTF 1
+
+/* Define to 1 when the gnulib module vprintf should be tested. */
+#define GNULIB_TEST_VPRINTF 1
+
+/* Define to 1 when the gnulib module wgetcwd should be tested. */
+#define GNULIB_TEST_WGETCWD 1
+
+/* Define to 1 when the gnulib module wmemcpy should be tested. */
+#define GNULIB_TEST_WMEMCPY 1
+
+/* Define to 1 when the gnulib module wmemset should be tested. */
+#define GNULIB_TEST_WMEMSET 1
+
+/* Define if the __locale_t type contains the name of the LC_MESSAGES
+ category. */
+/* #undef HAVE_AIX72_LOCALES */
+
+/* Define to 1 if you have 'alloca', as a function or macro. */
+#define HAVE_ALLOCA 1
+
+/* Define to 1 if <alloca.h> works. */
+#define HAVE_ALLOCA_H 1
+
+/* Define to 1 if you have the 'asprintf' function. */
+#define HAVE_ASPRINTF 1
+
+/* Define to 1 if you have the <bp-sym.h> header file. */
+/* #undef HAVE_BP_SYM_H */
+
+/* Define to 1 if the compiler understands __builtin_expect. */
+#define HAVE_BUILTIN_EXPECT 1
+
+/* Define to 1 if you have the Mac OS X function
+ CFLocaleCopyPreferredLanguages in the CoreFoundation framework. */
+#define HAVE_CFLOCALECOPYPREFERREDLANGUAGES 1
+
+/* Define to 1 if you have the Mac OS X function CFPreferencesCopyAppValue in
+ the CoreFoundation framework. */
+#define HAVE_CFPREFERENCESCOPYAPPVALUE 1
+
+/* Define if the copysignf function is declared in <math.h> and available in
+ libc. */
+/* #undef HAVE_COPYSIGNF_IN_LIBC */
+
+/* Define if the copysignl function is declared in <math.h> and available in
+ libc. */
+/* #undef HAVE_COPYSIGNL_IN_LIBC */
+
+/* Define if the copysign function is declared in <math.h> and available in
+ libc. */
+/* #undef HAVE_COPYSIGN_IN_LIBC */
+
+/* Define to 1 if you have the <crtdefs.h> header file. */
+/* #undef HAVE_CRTDEFS_H */
+
+/* Define to 1 if bool, true and false work as per C2023. */
+/* #undef HAVE_C_BOOL */
+
+/* Define to 1 if the static_assert keyword works. */
+/* #undef HAVE_C_STATIC_ASSERT */
+
+/* Define if the GNU dcgettext() function is already present or preinstalled.
+ */
+/* #undef HAVE_DCGETTEXT */
+
+/* Define to 1 if you have the declaration of 'alarm', and to 0 if you don't.
+ */
+#define HAVE_DECL_ALARM 1
+
+/* Define to 1 if you have the declaration of 'copysign', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_COPYSIGN */
+
+/* Define to 1 if you have the declaration of 'copysignf', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_COPYSIGNF */
+
+/* Define to 1 if you have the declaration of 'copysignl', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_COPYSIGNL */
+
+/* Define to 1 if you have the declaration of 'ecvt', and to 0 if you don't.
+ */
+#define HAVE_DECL_ECVT 1
+
+/* Define to 1 if you have the declaration of 'execvpe', and to 0 if you
+ don't. */
+#define HAVE_DECL_EXECVPE 0
+
+/* Define to 1 if you have the declaration of 'fcloseall', and to 0 if you
+ don't. */
+#define HAVE_DECL_FCLOSEALL 0
+
+/* Define to 1 if you have the declaration of 'fcvt', and to 0 if you don't.
+ */
+#define HAVE_DECL_FCVT 1
+
+/* Define to 1 if you have the declaration of 'feof_unlocked', and to 0 if you
+ don't. */
+#define HAVE_DECL_FEOF_UNLOCKED 1
+
+/* Define to 1 if you have the declaration of 'fgets_unlocked', and to 0 if
+ you don't. */
+#define HAVE_DECL_FGETS_UNLOCKED 0
+
+/* Define to 1 if you have the declaration of 'gcvt', and to 0 if you don't.
+ */
+#define HAVE_DECL_GCVT 1
+
+/* Define to 1 if you have the declaration of 'getw', and to 0 if you don't.
+ */
+#define HAVE_DECL_GETW 1
+
+/* Define to 1 if you have the declaration of 'mbrtowc', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_MBRTOWC */
+
+/* Define to 1 if you have the declaration of 'mbsinit', and to 0 if you
+ don't. */
+/* #undef HAVE_DECL_MBSINIT */
+
+/* Define to 1 if you have the declaration of 'putw', and to 0 if you don't.
+ */
+#define HAVE_DECL_PUTW 1
+
+/* Define to 1 if you have the declaration of 'wcsdup', and to 0 if you don't.
+ */
+#define HAVE_DECL_WCSDUP 1
+
+/* Define to 1 if you have the declaration of 'wcsnlen', and to 0 if you
+ don't. */
+#define HAVE_DECL_WCSNLEN 1
+
+/* Define to 1 if you have the declaration of '_snprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL__SNPRINTF 0
+
+/* Define to 1 if you have the declaration of '_snwprintf', and to 0 if you
+ don't. */
+#define HAVE_DECL__SNWPRINTF 0
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the `duplocale' function. */
+#define HAVE_DUPLOCALE 1
+
+/* Define if the locale_t type contains insufficient information, as on
+ OpenBSD. */
+/* #undef HAVE_FAKE_LOCALES */
+
+/* Define to 1 if you have the <features.h> header file. */
+/* #undef HAVE_FEATURES_H */
+
+/* Define to 1 if you have the `freelocale' function. */
+#define HAVE_FREELOCALE 1
+
+/* Define if the 'free' function is guaranteed to preserve errno. */
+/* #undef HAVE_FREE_POSIX */
+
+/* Define if the frexpl function is available in libc. */
+#define HAVE_FREXPL_IN_LIBC 1
+
+/* Define if the frexp function is available in libc. */
+#define HAVE_FREXP_IN_LIBC 1
+
+/* Define to 1 if you have the 'getcwd' function. */
+#define HAVE_GETCWD 1
+
+/* Define to 1 if you have the 'getegid' function. */
+#define HAVE_GETEGID 1
+
+/* Define to 1 if you have the 'geteuid' function. */
+#define HAVE_GETEUID 1
+
+/* Define to 1 if you have the 'getgid' function. */
+#define HAVE_GETGID 1
+
+/* Define to 1 if you have the 'getlocalename_l' function. */
+/* #undef HAVE_GETLOCALENAME_L */
+
+/* Define to 1 if you have the 'getpagesize' function. */
+#define HAVE_GETPAGESIZE 1
+
+/* Define if the GNU gettext() function is already present or preinstalled. */
+/* #undef HAVE_GETTEXT */
+
+/* Define to 1 if you have the 'getuid' function. */
+#define HAVE_GETUID 1
+
+/* Define if the uselocale function exists, may be safely called, and returns
+ sufficient information. */
+#define HAVE_GOOD_USELOCALE 1
+
+/* Define if you have the iconv() function and it works. */
+/* #undef HAVE_ICONV */
+
+/* Define if you have the 'intmax_t' type in <stdint.h> or <inttypes.h>. */
+#define HAVE_INTMAX_T 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define if <inttypes.h> exists, doesn't clash with <sys/types.h>, and
+ declares uintmax_t. */
+#define HAVE_INTTYPES_H_WITH_UINTMAX 1
+
+/* Define if the isnan(double) function is available in libc. */
+#define HAVE_ISNAND_IN_LIBC 1
+
+/* Define if the isnan(float) function is available in libc. */
+#define HAVE_ISNANF_IN_LIBC 1
+
+/* Define if the isnan(long double) function is available in libc. */
+#define HAVE_ISNANL_IN_LIBC 1
+
+/* Define if you have <langinfo.h> and nl_langinfo(CODESET). */
+#define HAVE_LANGINFO_CODESET 1
+
+/* Define to 1 if you have the <langinfo.h> header file. */
+#define HAVE_LANGINFO_H 1
+
+/* Define if your <locale.h> file defines LC_MESSAGES. */
+#define HAVE_LC_MESSAGES 1
+
+/* Define if the ldexpl function is available in libc. */
+#define HAVE_LDEXPL_IN_LIBC 1
+
+/* Define if the ldexp function is available in libc. */
+#define HAVE_LDEXP_IN_LIBC 1
+
+/* Define to 1 if you have the <limits.h> header file. */
+#define HAVE_LIMITS_H 1
+
+/* Define to 1 if the system has the type 'long long int'. */
+#define HAVE_LONG_LONG_INT 1
+
+/* Define to 1 if mmap()'s MAP_ANONYMOUS flag is available after including
+ config.h and <sys/mman.h>. */
+#define HAVE_MAP_ANONYMOUS 1
+
+/* Define to 1 if you have the <math.h> header file. */
+#define HAVE_MATH_H 1
+
+/* Define to 1 if you have the 'mbrtowc' function. */
+#define HAVE_MBRTOWC 1
+
+/* Define to 1 if you have the 'mbsinit' function. */
+#define HAVE_MBSINIT 1
+
+/* Define to 1 if <wchar.h> declares mbstate_t. */
+#define HAVE_MBSTATE_T 1
+
+/* Define to 1 if you have the 'mempcpy' function. */
+/* #undef HAVE_MEMPCPY */
+
+/* Define to 1 if you have the <minix/config.h> header file. */
+/* #undef HAVE_MINIX_CONFIG_H */
+
+/* Define to 1 if you have a working 'mmap' system call. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the 'mprotect' function. */
+#define HAVE_MPROTECT 1
+
+/* Define to 1 if you have the 'munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* Define if the locale_t type does not contain the name of each locale
+ category. */
+/* #undef HAVE_NAMELESS_LOCALES */
+
+/* Define to 1 if you have the 'newlocale' function. */
+#define HAVE_NEWLOCALE 1
+
+/* Define to 1 if you have the `nl_langinfo' function. */
+#define HAVE_NL_LANGINFO 1
+
+/* Define if your printf() function supports format strings with positions. */
+#define HAVE_POSIX_PRINTF 1
+
+/* Define if you have the <pthread.h> header and the POSIX threads API. */
+#define HAVE_PTHREAD_API 1
+
+/* Define to 1 if you have the <pthread.h> header file. */
+#define HAVE_PTHREAD_H 1
+
+/* Define if the <pthread.h> defines PTHREAD_MUTEX_RECURSIVE. */
+#define HAVE_PTHREAD_MUTEX_RECURSIVE 1
+
+/* Define if the POSIX multithreading library has read/write locks. */
+#define HAVE_PTHREAD_RWLOCK 1
+
+/* Define if the 'pthread_rwlock_rdlock' function prefers a writer to a
+ reader. */
+#define HAVE_PTHREAD_RWLOCK_RDLOCK_PREFER_WRITER 1
+
+/* Define to 1 if the system has the type 'pthread_spinlock_t'. */
+/* #undef HAVE_PTHREAD_SPINLOCK_T */
+
+/* Define to 1 if the system has the type 'pthread_t'. */
+#define HAVE_PTHREAD_T 1
+
+/* Define to 1 if 'long double' and 'double' have the same representation. */
+#define HAVE_SAME_LONG_DOUBLE_AS_DOUBLE 1
+
+/* Define to 1 if you have the <sched.h> header file. */
+#define HAVE_SCHED_H 1
+
+/* Define to 1 if you have the <search.h> header file. */
+#define HAVE_SEARCH_H 1
+
+/* Define to 1 if 'sig_atomic_t' is a signed integer type. */
+/* #undef HAVE_SIGNED_SIG_ATOMIC_T */
+
+/* Define to 1 if 'wchar_t' is a signed integer type. */
+/* #undef HAVE_SIGNED_WCHAR_T */
+
+/* Define to 1 if 'wint_t' is a signed integer type. */
+/* #undef HAVE_SIGNED_WINT_T */
+
+/* Define to 1 if you have the 'snprintf' function. */
+#define HAVE_SNPRINTF 1
+
+/* Define if the return value of the snprintf function is the number of of
+ bytes (excluding the terminating NUL) that would have been produced if the
+ buffer had been large enough. */
+#define HAVE_SNPRINTF_RETVAL_C99 1
+
+/* Define if the string produced by the snprintf function is always NUL
+ terminated. */
+#define HAVE_SNPRINTF_TRUNCATION_C99 1
+
+/* Define if the locale_t type is as on Solaris 11.4. */
+/* #undef HAVE_SOLARIS114_LOCALES */
+
+/* Define to 1 if you have the <stdbool.h> header file. */
+#define HAVE_STDBOOL_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define if <stdint.h> exists, doesn't clash with <sys/types.h>, and declares
+ uintmax_t. */
+#define HAVE_STDINT_H_WITH_UINTMAX 1
+
+/* Define to 1 if you have the <stdio.h> header file. */
+#define HAVE_STDIO_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the 'stpcpy' function. */
+#define HAVE_STPCPY 1
+
+/* Define to 1 if you have the 'strcasecmp' function. */
+#define HAVE_STRCASECMP 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the 'strnlen' function. */
+#define HAVE_STRNLEN 1
+
+/* Define to 1 if you have the 'swprintf' function. */
+#define HAVE_SWPRINTF 1
+
+/* Define to 1 if you have the 'symlink' function. */
+#define HAVE_SYMLINK 1
+
+/* Define to 1 if you have the <sys/bitypes.h> header file. */
+/* #undef HAVE_SYS_BITYPES_H */
+
+/* Define to 1 if you have the <sys/inttypes.h> header file. */
+/* #undef HAVE_SYS_INTTYPES_H */
+
+/* Define to 1 if you have the <sys/mman.h> header file. */
+#define HAVE_SYS_MMAN_H 1
+
+/* Define to 1 if you have the <sys/param.h> header file. */
+#define HAVE_SYS_PARAM_H 1
+
+/* Define to 1 if you have the <sys/single_threaded.h> header file. */
+/* #undef HAVE_SYS_SINGLE_THREADED_H */
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#define HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the `thrd_create' function. */
+/* #undef HAVE_THRD_CREATE */
+
+/* Define to 1 if you have the <threads.h> header file. */
+/* #undef HAVE_THREADS_H */
+
+/* Define to 1 if you have the `tsearch' function. */
+#define HAVE_TSEARCH 1
+
+/* Define to 1 if you have the `twalk' function. */
+#define HAVE_TWALK 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to 1 if the system has the type 'unsigned long long int'. */
+#define HAVE_UNSIGNED_LONG_LONG_INT 1
+
+/* Define to 1 if you have the 'uselocale' function. */
+#define HAVE_USELOCALE 1
+
+/* Define to 1 if you have the 'vasnprintf' function. */
+/* #undef HAVE_VASNPRINTF */
+
+/* Define to 1 or 0, depending whether the compiler supports simple visibility
+ declarations. */
+#define HAVE_VISIBILITY 1
+
+/* Define to 1 if you have the <wchar.h> header file. */
+#define HAVE_WCHAR_H 1
+
+/* Define to 1 if you have the 'wcrtomb' function. */
+#define HAVE_WCRTOMB 1
+
+/* Define to 1 if you have the 'wcslen' function. */
+#define HAVE_WCSLEN 1
+
+/* Define to 1 if you have the 'wcsnlen' function. */
+#define HAVE_WCSNLEN 1
+
+/* Define to 1 if the compiler and linker support weak declarations of
+ symbols. */
+/* #undef HAVE_WEAK_SYMBOLS */
+
+/* Define to 1 if <locale.h> defines the _locale_t type. */
+/* #undef HAVE_WINDOWS_LOCALE_T */
+
+/* Define if you have the 'wint_t' type. */
+#define HAVE_WINT_T 1
+
+/* Define to 1 if O_NOATIME works. */
+#define HAVE_WORKING_O_NOATIME 1
+
+/* Define to 1 if O_NOFOLLOW works. */
+#define HAVE_WORKING_O_NOFOLLOW 1
+
+/* Define if the swprintf function works correctly when it produces output
+ that contains null wide characters. */
+/* #undef HAVE_WORKING_SWPRINTF */
+
+/* Define if the uselocale function exists and may safely be called. */
+#define HAVE_WORKING_USELOCALE 1
+
+/* Define to 1 if you have the 'wprintf' function. */
+#define HAVE_WPRINTF 1
+
+/* Define to 1 if you have the <xlocale.h> header file. */
+#define HAVE_XLOCALE_H 1
+
+/* Define to 1 if you have the '__fsetlocking' function. */
+/* #undef HAVE___FSETLOCKING */
+
+/* Define to 1 if ctype.h defines __header_inline. */
+#define HAVE___HEADER_INLINE 1
+
+/* Please see the Gnulib manual for how to use these macros.
+
+ Suppress extern inline with HP-UX cc, as it appears to be broken; see
+ <https://lists.gnu.org/r/bug-texinfo/2013-02/msg00030.html>.
+
+ Suppress extern inline with Sun C in standards-conformance mode, as it
+ mishandles inline functions that call each other. E.g., for 'inline void f
+ (void) { } inline void g (void) { f (); }', c99 incorrectly complains
+ 'reference to static identifier "f" in extern inline function'.
+ This bug was observed with Oracle Developer Studio 12.6
+ (Sun C 5.15 SunOS_sparc 2017/05/30).
+
+ Suppress extern inline (with or without __attribute__ ((__gnu_inline__)))
+ on configurations that mistakenly use 'static inline' to implement
+ functions or macros in standard C headers like <ctype.h>. For example,
+ if isdigit is mistakenly implemented via a static inline function,
+ a program containing an extern inline function that calls isdigit
+ may not work since the C standard prohibits extern inline functions
+ from calling static functions (ISO C 99 section 6.7.4.(3).
+ This bug is known to occur on:
+
+ OS X 10.8 and earlier; see:
+ https://lists.gnu.org/r/bug-gnulib/2012-12/msg00023.html
+
+ DragonFly; see
+ http://muscles.dragonflybsd.org/bulk/clang-master-potential/20141111_102002/logs/ah-tty-0.3.12.log
+
+ FreeBSD; see:
+ https://lists.gnu.org/r/bug-gnulib/2014-07/msg00104.html
+
+ OS X 10.9 has a macro __header_inline indicating the bug is fixed for C and
+ for clang but remains for g++; see <https://trac.macports.org/ticket/41033>.
+ Assume DragonFly and FreeBSD will be similar.
+
+ GCC 4.3 and above with -std=c99 or -std=gnu99 implements ISO C99
+ inline semantics, unless -fgnu89-inline is used. It defines a macro
+ __GNUC_STDC_INLINE__ to indicate this situation or a macro
+ __GNUC_GNU_INLINE__ to indicate the opposite situation.
+ GCC 4.2 with -std=c99 or -std=gnu99 implements the GNU C inline
+ semantics but warns, unless -fgnu89-inline is used:
+ warning: C99 inline functions are not supported; using GNU89
+ warning: to disable this warning use -fgnu89-inline or the gnu_inline
+ function attribute It defines a macro __GNUC_GNU_INLINE__ to indicate this
+ situation.
+ */
+#if (((defined __APPLE__ && defined __MACH__) || defined __DragonFly__ || \
+ defined __FreeBSD__) && \
+ (defined HAVE___HEADER_INLINE \
+ ? (defined __cplusplus && defined __GNUC_STDC_INLINE__ && \
+ !defined __clang__) \
+ : ((!defined _DONT_USE_CTYPE_INLINE_ && \
+ (defined __GNUC__ || defined __cplusplus)) || \
+ (defined _FORTIFY_SOURCE && 0 < _FORTIFY_SOURCE && \
+ defined __GNUC__ && !defined __cplusplus))))
+#define _GL_EXTERN_INLINE_STDHEADER_BUG
+#endif
+#if ((__GNUC__ ? (defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__ && \
+ !defined __PCC__) \
+ : (199901L <= __STDC_VERSION__ && !defined __HP_cc && \
+ !defined __PGI && !(defined __SUNPRO_C && __STDC__))) && \
+ !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
+#define _GL_INLINE inline
+#define _GL_EXTERN_INLINE extern inline
+#define _GL_EXTERN_INLINE_IN_USE
+#elif (2 < __GNUC__ + (7 <= __GNUC_MINOR__) && !defined __STRICT_ANSI__ && \
+ !defined __PCC__ && !defined _GL_EXTERN_INLINE_STDHEADER_BUG)
+#if defined __GNUC_GNU_INLINE__ && __GNUC_GNU_INLINE__
+/* __gnu_inline__ suppresses a GCC 4.2 diagnostic. */
+#define _GL_INLINE extern inline __attribute__((__gnu_inline__))
+#else
+#define _GL_INLINE extern inline
+#endif
+#define _GL_EXTERN_INLINE extern
+#define _GL_EXTERN_INLINE_IN_USE
+#else
+#define _GL_INLINE _GL_UNUSED static
+#define _GL_EXTERN_INLINE _GL_UNUSED static
+#endif
+
+/* In GCC 4.6 (inclusive) to 5.1 (exclusive),
+ suppress bogus "no previous prototype for 'FOO'"
+ and "no previous declaration for 'FOO'" diagnostics,
+ when FOO is an inline function in the header; see
+ <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=54113> and
+ <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63877>. */
+#if __GNUC__ == 4 && 6 <= __GNUC_MINOR__
+#if defined __GNUC_STDC_INLINE__ && __GNUC_STDC_INLINE__
+#define _GL_INLINE_HEADER_CONST_PRAGMA
+#else
+#define _GL_INLINE_HEADER_CONST_PRAGMA \
+ _Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=const\"")
+#endif
+#define _GL_INLINE_HEADER_BEGIN \
+ _Pragma("GCC diagnostic push") \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-prototypes\"") \
+ _Pragma("GCC diagnostic ignored \"-Wmissing-declarations\"") \
+ _GL_INLINE_HEADER_CONST_PRAGMA
+#define _GL_INLINE_HEADER_END _Pragma("GCC diagnostic pop")
+#else
+#define _GL_INLINE_HEADER_BEGIN
+#define _GL_INLINE_HEADER_END
+#endif
+
+/* Define as const if the declaration of iconv() needs const. */
+#define ICONV_CONST
+
+/* Define as the bit index in the word where to find bit 0 of the exponent of
+ 'long double'. */
+#define LDBL_EXPBIT0_BIT 20
+
+/* Define as the word index where to find the exponent of 'long double'. */
+#define LDBL_EXPBIT0_WORD 1
+
+/* Define as the bit index in the word where to find the sign of 'long
+ double'. */
+/* #undef LDBL_SIGNBIT_BIT */
+
+/* Define as the word index where to find the sign of 'long double'. */
+/* #undef LDBL_SIGNBIT_WORD */
+
+/* Define if localename.c overrides newlocale(), duplocale(), freelocale(). */
+/* #undef LOCALENAME_ENHANCE_LOCALE_FUNCS */
+
+/* Define to the sub-directory where libtool stores uninstalled libraries. */
+#define LT_OBJDIR ".libs/"
+
+/* Define to a substitute value for mmap()'s MAP_ANONYMOUS flag. */
+/* #undef MAP_ANONYMOUS */
+
+/* Define if the mbrtowc function does not return (size_t) -2 for empty input.
+ */
+/* #undef MBRTOWC_EMPTY_INPUT_BUG */
+
+/* Define if the mbrtowc function may signal encoding errors in the C locale.
+ */
+/* #undef MBRTOWC_IN_C_LOCALE_MAYBE_EILSEQ */
+
+/* Define if the mbrtowc function has the NULL pwc argument bug. */
+/* #undef MBRTOWC_NULL_ARG1_BUG */
+
+/* Define if the mbrtowc function has the NULL string argument bug. */
+/* #undef MBRTOWC_NULL_ARG2_BUG */
+
+/* Define if the mbrtowc function does not return 0 for a NUL character. */
+/* #undef MBRTOWC_NUL_RETVAL_BUG */
+
+/* Define if the mbrtowc function returns a wrong return value. */
+/* #undef MBRTOWC_RETVAL_BUG */
+
+/* Define if the mbrtowc function stores a wide character when reporting
+ incomplete input. */
+/* #undef MBRTOWC_STORES_INCOMPLETE_BUG */
+
+/* Use GNU style printf and scanf. */
+#ifndef __USE_MINGW_ANSI_STDIO
+#define __USE_MINGW_ANSI_STDIO 1
+#endif
+
+/* Define to 1 on musl libc. */
+/* #undef MUSL_LIBC */
+
+/* Define if the vasnprintf implementation needs special code for the 'a' and
+ 'A' directives. */
+#define NEED_PRINTF_DIRECTIVE_A 1
+
+/* Define if the vasnprintf implementation needs special code for the 'b'
+ directive. */
+#define NEED_PRINTF_DIRECTIVE_B 1
+
+/* Define if the vasnprintf implementation needs special code for the 'F'
+ directive. */
+/* #undef NEED_PRINTF_DIRECTIVE_F */
+
+/* Define if the vasnprintf implementation needs special code for the 'lc'
+ directive. */
+/* #undef NEED_PRINTF_DIRECTIVE_LC */
+
+/* Define if the vasnprintf implementation needs special code for the 'ls'
+ directive. */
+/* #undef NEED_PRINTF_DIRECTIVE_LS */
+
+/* Define if the vasnprintf implementation needs special code for 'double'
+ arguments. */
+#define NEED_PRINTF_DOUBLE 1
+
+/* Define if the vasnprintf implementation needs special code for surviving
+ out-of-memory conditions. */
+#define NEED_PRINTF_ENOMEM 1
+
+/* Define if the vasnprintf implementation needs special code for the # flag
+ with a zero precision and a zero value in the 'x' and 'X' directives. */
+/* #undef NEED_PRINTF_FLAG_ALT_PRECISION_ZERO */
+
+/* Define if the vasnprintf implementation needs special code for the ' flag.
+ */
+/* #undef NEED_PRINTF_FLAG_GROUPING */
+
+/* Define if the vasnprintf implementation needs special code for the '-'
+ flag. */
+/* #undef NEED_PRINTF_FLAG_LEFTADJUST */
+
+/* Define if the vasnprintf implementation needs special code for the 0 flag.
+ */
+/* #undef NEED_PRINTF_FLAG_ZERO */
+
+/* Define if the vasnprintf implementation needs special code for infinite
+ 'double' arguments. */
+/* #undef NEED_PRINTF_INFINITE_DOUBLE */
+
+/* Define if the vasnprintf implementation needs special code for infinite
+ 'long double' arguments. */
+/* #undef NEED_PRINTF_INFINITE_LONG_DOUBLE */
+
+/* Define if the vasnprintf implementation needs special code for 'long
+ double' arguments. */
+#define NEED_PRINTF_LONG_DOUBLE 1
+
+/* Define if the vasnprintf implementation needs special code for supporting
+ large precisions without arbitrary bounds. */
+/* #undef NEED_PRINTF_UNBOUNDED_PRECISION */
+
+/* Define if the vasnwprintf implementation needs special code for the 'c'
+ directive. */
+#define NEED_WPRINTF_DIRECTIVE_C 1
+
+/* Define if the vasnwprintf implementation needs special code for the 'a'
+ directive with 'long double' arguments. */
+/* #undef NEED_WPRINTF_DIRECTIVE_LA */
+
+/* Define if the vasnwprintf implementation needs special code for the 'lc'
+ directive. */
+#define NEED_WPRINTF_DIRECTIVE_LC 1
+
+/* Name of package */
+#define PACKAGE "libintl"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT "bug-gettext@gnu.org"
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "libintl"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "libintl 0.24"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "libintl"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.24"
+
+/* Define if the pthread_in_use() detection is hard. */
+/* #undef PTHREAD_IN_USE_DETECTION_HARD */
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'ptrdiff_t'. */
+/* #undef PTRDIFF_T_SUFFIX */
+
+/* Define if vasnprintf exists but is overridden by gnulib. */
+/* #undef REPLACE_VASNPRINTF */
+
+/* Define to 1 if setlocale (LC_ALL, NULL) is multithread-safe. */
+#define SETLOCALE_NULL_ALL_MTSAFE 0
+
+/* Define to 1 if setlocale (category, NULL) is multithread-safe. */
+#define SETLOCALE_NULL_ONE_MTSAFE 1
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'sig_atomic_t'. */
+/* #undef SIG_ATOMIC_T_SUFFIX */
+
+/* Define as the maximum value of type 'size_t', if the system doesn't define
+ it. */
+#ifndef SIZE_MAX
+/* # undef SIZE_MAX */
+#endif
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'size_t'. */
+/* #undef SIZE_T_SUFFIX */
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at runtime.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown */
+/* #undef STACK_DIRECTION */
+
+/* Define to 1 if all of the C89 standard headers exist (not just the ones
+ required in a freestanding environment). This macro is provided for
+ backward compatibility; new code need not use it. */
+#define STDC_HEADERS 1
+
+/* Define if the combination of the ISO C and POSIX multithreading APIs can be
+ used. */
+/* #undef USE_ISOC_AND_POSIX_THREADS */
+
+/* Define if the ISO C multithreading library can be used. */
+/* #undef USE_ISOC_THREADS */
+
+/* Define to enable the declarations of ISO C 23 Annex K types and functions. */
+#if !(defined __STDC_WANT_LIB_EXT1__ && __STDC_WANT_LIB_EXT1__)
+#undef /**/ __STDC_WANT_LIB_EXT1__
+#define __STDC_WANT_LIB_EXT1__ 1
+#endif
+
+/* Define if the POSIX multithreading library can be used. */
+#define USE_POSIX_THREADS 1
+
+/* Define if references to the POSIX multithreading library are satisfied by
+ libc. */
+/* #undef USE_POSIX_THREADS_FROM_LIBC */
+
+/* Define if references to the POSIX multithreading library should be made
+ weak. */
+/* #undef USE_POSIX_THREADS_WEAK */
+
+/* Enable extensions on AIX, Interix, z/OS. */
+#ifndef _ALL_SOURCE
+#define _ALL_SOURCE 1
+#endif
+/* Enable general extensions on macOS. */
+#ifndef _DARWIN_C_SOURCE
+#define _DARWIN_C_SOURCE 1
+#endif
+/* Enable general extensions on Solaris. */
+#ifndef __EXTENSIONS__
+#define __EXTENSIONS__ 1
+#endif
+/* Enable GNU extensions on systems that have them. */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE 1
+#endif
+/* Enable X/Open compliant socket functions that do not require linking
+ with -lxnet on HP-UX 11.11. */
+#ifndef _HPUX_ALT_XOPEN_SOCKET_API
+#define _HPUX_ALT_XOPEN_SOCKET_API 1
+#endif
+/* Identify the host operating system as Minix.
+ This macro does not affect the system headers' behavior.
+ A future release of Autoconf may stop defining this macro. */
+#ifndef _MINIX
+/* # undef _MINIX */
+#endif
+/* Enable general extensions on NetBSD.
+ Enable NetBSD compatibility extensions on Minix. */
+#ifndef _NETBSD_SOURCE
+#define _NETBSD_SOURCE 1
+#endif
+/* Enable OpenBSD compatibility extensions on NetBSD.
+ Oddly enough, this does nothing on OpenBSD. */
+#ifndef _OPENBSD_SOURCE
+#define _OPENBSD_SOURCE 1
+#endif
+/* Define to 1 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_SOURCE
+/* # undef _POSIX_SOURCE */
+#endif
+/* Define to 2 if needed for POSIX-compatible behavior. */
+#ifndef _POSIX_1_SOURCE
+/* # undef _POSIX_1_SOURCE */
+#endif
+/* Enable POSIX-compatible threading on Solaris. */
+#ifndef _POSIX_PTHREAD_SEMANTICS
+#define _POSIX_PTHREAD_SEMANTICS 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-5:2014. */
+#ifndef __STDC_WANT_IEC_60559_ATTRIBS_EXT__
+#define __STDC_WANT_IEC_60559_ATTRIBS_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-1:2014. */
+#ifndef __STDC_WANT_IEC_60559_BFP_EXT__
+#define __STDC_WANT_IEC_60559_BFP_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-2:2015. */
+#ifndef __STDC_WANT_IEC_60559_DFP_EXT__
+#define __STDC_WANT_IEC_60559_DFP_EXT__ 1
+#endif
+/* Enable extensions specified by C23 Annex F. */
+#ifndef __STDC_WANT_IEC_60559_EXT__
+#define __STDC_WANT_IEC_60559_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TS 18661-4:2015. */
+#ifndef __STDC_WANT_IEC_60559_FUNCS_EXT__
+#define __STDC_WANT_IEC_60559_FUNCS_EXT__ 1
+#endif
+/* Enable extensions specified by C23 Annex H and ISO/IEC TS 18661-3:2015. */
+#ifndef __STDC_WANT_IEC_60559_TYPES_EXT__
+#define __STDC_WANT_IEC_60559_TYPES_EXT__ 1
+#endif
+/* Enable extensions specified by ISO/IEC TR 24731-2:2010. */
+#ifndef __STDC_WANT_LIB_EXT2__
+#define __STDC_WANT_LIB_EXT2__ 1
+#endif
+/* Enable extensions specified by ISO/IEC 24747:2009. */
+#ifndef __STDC_WANT_MATH_SPEC_FUNCS__
+#define __STDC_WANT_MATH_SPEC_FUNCS__ 1
+#endif
+/* Enable extensions on HP NonStop. */
+#ifndef _TANDEM_SOURCE
+#define _TANDEM_SOURCE 1
+#endif
+/* Enable X/Open extensions. Define to 500 only if necessary
+ to make mbstate_t available. */
+#ifndef _XOPEN_SOURCE
+/* # undef _XOPEN_SOURCE */
+#endif
+
+/* Define if the native Windows multithreading API can be used. */
+/* #undef USE_WINDOWS_THREADS */
+
+/* Version number of package */
+#define VERSION "0.24"
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'wchar_t'. */
+/* #undef WCHAR_T_SUFFIX */
+
+/* Define to l, ll, u, ul, ull, etc., as suitable for constants of type
+ 'wint_t'. */
+/* #undef WINT_T_SUFFIX */
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+ significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+#if defined __BIG_ENDIAN__
+#define WORDS_BIGENDIAN 1
+#endif
+#else
+#ifndef WORDS_BIGENDIAN
+/* # undef WORDS_BIGENDIAN */
+#endif
+#endif
+
+/* True if the compiler says it groks GNU C version MAJOR.MINOR.
+ Except that
+ - clang groks GNU C 4.2, even on Windows, where it does not define
+ __GNUC__.
+ - The OpenMandriva-modified clang compiler pretends that it groks
+ GNU C version 13.1, but it doesn't: It does not support
+ __attribute__ ((__malloc__ (f, i))), nor does it support
+ __attribute__ ((__warning__ (message))) on a function redeclaration.
+ - Users can make clang lie as well, through the -fgnuc-version option. */
+#if defined __GNUC__ && defined __GNUC_MINOR__ && !defined __clang__
+#define _GL_GNUC_PREREQ(major, minor) \
+ ((major) < __GNUC__ + ((minor) <= __GNUC_MINOR__))
+#elif defined __clang__
+/* clang really only groks GNU C 4.2. */
+#define _GL_GNUC_PREREQ(major, minor) ((major) < 4 + ((minor) <= 2))
+#else
+#define _GL_GNUC_PREREQ(major, minor) 0
+#endif
+
+/* Define to enable the declarations of ISO C 11 types and functions. */
+/* #undef _ISOC11_SOURCE */
+
+/* Define to 1 on Solaris. */
+/* #undef _LCONV_C99 */
+
+/* Define so that AIX headers are more compatible with GNU/Linux. */
+#define _LINUX_SOURCE_COMPAT 1
+
+/* The _Noreturn keyword of C11. */
+#ifndef _Noreturn
+#if (defined __cplusplus && \
+ ((201103 <= __cplusplus && !(__GNUC__ == 4 && __GNUC_MINOR__ == 7)) || \
+ (defined _MSC_VER && 1900 <= _MSC_VER)) && \
+ 0)
+/* [[noreturn]] is not practically usable, because with it the syntax
+ extern _Noreturn void func (...);
+ would not be valid; such a declaration would only be valid with 'extern'
+ and '_Noreturn' swapped, or without the 'extern' keyword. However, some
+ AIX system header files and several gnulib header files use precisely
+ this syntax with 'extern'. */
+#define _Noreturn [[noreturn]]
+#elif (defined __clang__ && __clang_major__ < 16 && \
+ defined _GL_WORK_AROUND_LLVM_BUG_59792)
+/* Compile with -D_GL_WORK_AROUND_LLVM_BUG_59792 to work around
+ that rare LLVM bug, though you may get many false-alarm warnings. */
+#define _Noreturn
+#elif ((!defined __cplusplus || defined __clang__) && \
+ (201112 <= (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) || \
+ (!defined __STRICT_ANSI__ && \
+ (_GL_GNUC_PREREQ(4, 7) || \
+ (defined __apple_build_version__ \
+ ? 6000000 <= __apple_build_version__ \
+ : 3 < __clang_major__ + (5 <= __clang_minor__))))))
+/* _Noreturn works as-is. */
+#elif _GL_GNUC_PREREQ(2, 8) || defined __clang__ || 0x5110 <= __SUNPRO_C
+#define _Noreturn __attribute__((__noreturn__))
+#elif 1200 <= (defined _MSC_VER ? _MSC_VER : 0)
+#define _Noreturn __declspec(noreturn)
+#else
+#define _Noreturn
+#endif
+#endif
+
+/* For standard stat data types on VMS. */
+#define _USE_STD_STAT 1
+
+/* Define to 1 if the system <stdint.h> predates C++11. */
+/* #undef __STDC_CONSTANT_MACROS */
+
+/* Define to 1 if the system <stdint.h> predates C++11. */
+/* #undef __STDC_LIMIT_MACROS */
+
+/* The _GL_ASYNC_SAFE marker should be attached to functions that are
+ signal handlers (for signals other than SIGABRT, SIGPIPE) or can be
+ invoked from such signal handlers. Such functions have some restrictions:
+ * All functions that it calls should be marked _GL_ASYNC_SAFE as well,
+ or should be listed as async-signal-safe in POSIX
+ <https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_04>
+ section 2.4.3. Note that malloc(), sprintf(), and fwrite(), in
+ particular, are NOT async-signal-safe.
+ * All memory locations (variables and struct fields) that these functions
+ access must be marked 'volatile'. This holds for both read and write
+ accesses. Otherwise the compiler might optimize away stores to and
+ reads from such locations that occur in the program, depending on its
+ data flow analysis. For example, when the program contains a loop
+ that is intended to inspect a variable set from within a signal handler
+ while (!signal_occurred)
+ ;
+ the compiler is allowed to transform this into an endless loop if the
+ variable 'signal_occurred' is not declared 'volatile'.
+ Additionally, recall that:
+ * A signal handler should not modify errno (except if it is a handler
+ for a fatal signal and ends by raising the same signal again, thus
+ provoking the termination of the process). If it invokes a function
+ that may clobber errno, it needs to save and restore the value of
+ errno. */
+#define _GL_ASYNC_SAFE
+
+/* Attributes. */
+/* Define _GL_HAS_ATTRIBUTE only once, because on FreeBSD, with gcc < 5, if
+ <config.h> gets included once again after <sys/cdefs.h>, __has_attribute(x)
+ expands to 0 always, and redefining _GL_HAS_ATTRIBUTE would turn off all
+ attributes. */
+#ifndef _GL_HAS_ATTRIBUTE
+#if (defined __has_attribute && \
+ (!defined __clang_minor__ || \
+ (defined __apple_build_version__ ? 7000000 <= __apple_build_version__ \
+ : 5 <= __clang_major__)))
+#define _GL_HAS_ATTRIBUTE(attr) __has_attribute(__##attr##__)
+#else
+#define _GL_HAS_ATTRIBUTE(attr) _GL_ATTR_##attr
+#define _GL_ATTR_alloc_size _GL_GNUC_PREREQ(4, 3)
+#define _GL_ATTR_always_inline _GL_GNUC_PREREQ(3, 2)
+#define _GL_ATTR_artificial _GL_GNUC_PREREQ(4, 3)
+#define _GL_ATTR_cold _GL_GNUC_PREREQ(4, 3)
+#define _GL_ATTR_const _GL_GNUC_PREREQ(2, 95)
+#define _GL_ATTR_deprecated _GL_GNUC_PREREQ(3, 1)
+#define _GL_ATTR_diagnose_if 0
+#define _GL_ATTR_error _GL_GNUC_PREREQ(4, 3)
+#define _GL_ATTR_externally_visible _GL_GNUC_PREREQ(4, 1)
+#define _GL_ATTR_fallthrough _GL_GNUC_PREREQ(7, 0)
+#define _GL_ATTR_format _GL_GNUC_PREREQ(2, 7)
+#define _GL_ATTR_leaf _GL_GNUC_PREREQ(4, 6)
+#define _GL_ATTR_malloc _GL_GNUC_PREREQ(3, 0)
+#ifdef _ICC
+#define _GL_ATTR_may_alias 0
+#else
+#define _GL_ATTR_may_alias _GL_GNUC_PREREQ(3, 3)
+#endif
+#define _GL_ATTR_noinline _GL_GNUC_PREREQ(3, 1)
+#define _GL_ATTR_nonnull _GL_GNUC_PREREQ(3, 3)
+#define _GL_ATTR_nonstring _GL_GNUC_PREREQ(8, 0)
+#define _GL_ATTR_nothrow _GL_GNUC_PREREQ(3, 3)
+#define _GL_ATTR_packed _GL_GNUC_PREREQ(2, 7)
+#define _GL_ATTR_pure _GL_GNUC_PREREQ(2, 96)
+#define _GL_ATTR_reproducible 0 /* not yet supported, as of GCC 14 */
+#define _GL_ATTR_returns_nonnull _GL_GNUC_PREREQ(4, 9)
+#define _GL_ATTR_sentinel _GL_GNUC_PREREQ(4, 0)
+#define _GL_ATTR_unsequenced 0 /* not yet supported, as of GCC 14 */
+#define _GL_ATTR_unused _GL_GNUC_PREREQ(2, 7)
+#define _GL_ATTR_warn_unused_result _GL_GNUC_PREREQ(3, 4)
+#endif
+#endif
+
+/* Use __has_c_attribute if available. However, do not use with
+ pre-C23 GCC, which can issue false positives if -Wpedantic. */
+#if (defined __has_c_attribute && \
+ !(_GL_GNUC_PREREQ(4, 6) && \
+ (defined __STDC_VERSION__ ? __STDC_VERSION__ : 0) <= 201710))
+#define _GL_HAVE___HAS_C_ATTRIBUTE 1
+#else
+#define _GL_HAVE___HAS_C_ATTRIBUTE 0
+#endif
+
+/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...))
+ syntax, in function declarations. There are two problems here.
+ (Last tested with gcc/g++ 14 and clang/clang++ 18.)
+
+ 1) We want that the _GL_ATTRIBUTE_* can be cumulated on the same declaration
+ in any order.
+ =========================== foo.c = foo.cc ===========================
+ __attribute__ ((__deprecated__)) [[__nodiscard__]] int bar1 (int);
+ [[__nodiscard__]] __attribute__ ((__deprecated__)) int bar2 (int);
+ ======================================================================
+ This gives a syntax error
+ - in C mode with gcc
+ <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108796>, and
+ - in C++ mode with clang++ version < 16, and
+ - in C++ mode, inside extern "C" {}, still in newer clang++ versions
+ <https://github.com/llvm/llvm-project/issues/101990>.
+ */
+/* Define if, in a function declaration, the attributes in bracket syntax
+ [[...]] must come before the attributes in __attribute__((...)) syntax.
+ If this is defined, it is best to avoid the bracket syntax, so that the
+ various _GL_ATTRIBUTE_* can be cumulated on the same declaration in any
+ order. */
+#ifdef __cplusplus
+#if defined __clang__
+#define _GL_BRACKET_BEFORE_ATTRIBUTE 1
+#endif
+#else
+#if defined __GNUC__ && !defined __clang__
+#define _GL_BRACKET_BEFORE_ATTRIBUTE 1
+#endif
+#endif
+/*
+ 2) We want that the _GL_ATTRIBUTE_* can be placed in a declaration
+ - without 'extern', in C as well as in C++,
+ - with 'extern', in C,
+ - with 'extern "C"', in C++
+ in the same position. That is, we don't want to be forced to use a
+ macro which arranges for the attribute to come before 'extern' in
+ one case and after 'extern' in the other case, because such a macro
+ would make the source code of .h files pretty ugly.
+ =========================== foo.c = foo.cc ===========================
+ #ifdef __cplusplus
+ # define CC "C"
+ #else
+ # define CC
+ #endif
+
+ #define ND [[__nodiscard__]]
+ #define WUR __attribute__((__warn_unused_result__))
+
+ #ifdef __cplusplus
+ extern "C" {
+ #endif
+ // gcc clang g++ clang++
+
+ ND int foo (int);
+ int ND foo (int); // warn error warn error
+ int foo ND (int);
+ int foo (int) ND; // warn error warn error
+
+ WUR int foo (int);
+ int WUR foo (int);
+ int fo1 WUR (int); // error error error error
+ int foo (int) WUR;
+
+ #ifdef __cplusplus
+ }
+ #endif
+
+ // gcc clang g++ clang++
+
+ ND extern CC int foo (int); // error error
+ extern CC ND int foo (int); // error error
+ extern CC int ND foo (int); // warn error warn error
+ extern CC int foo ND (int);
+ extern CC int foo (int) ND; // warn error warn error
+
+ WUR extern CC int foo (int); // warn
+ extern CC WUR int foo (int);
+ extern CC int WUR foo (int);
+ extern CC int foo WUR (int); // error error error error
+ extern CC int foo (int) WUR;
+
+ ND EXTERN_C_FUNC int foo (int); // error error
+ EXTERN_C_FUNC ND int foo (int);
+ EXTERN_C_FUNC int ND foo (int); // warn error warn error
+ EXTERN_C_FUNC int foo ND (int);
+ EXTERN_C_FUNC int foo (int) ND; // warn error warn error
+
+ WUR EXTERN_C_FUNC int foo (int); // warn
+ EXTERN_C_FUNC WUR int foo (int);
+ EXTERN_C_FUNC int WUR foo (int);
+ EXTERN_C_FUNC int fo2 WUR (int); // error error error error
+ EXTERN_C_FUNC int foo (int) WUR;
+ ======================================================================
+ So, if we insist on using the 'extern' keyword ('extern CC' idiom):
+ * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]]
+ in both C and C++, there is one available position:
+ - between the function name and the parameter list.
+ * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax
+ in both C and C++, there are several available positions:
+ - before the return type,
+ - between return type and function name,
+ - at the end of the declaration.
+ * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] in C and to
+ __attribute__((...)) syntax in C++, there is no available position:
+ it would need to come before 'extern' in C but after 'extern "C"'
+ in C++.
+ * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax in C and
+ to bracket syntax [[...]] in C++, there is one available position:
+ - before the return type.
+ Whereas, if we use the 'EXTERN_C_FUNC' idiom, which conditionally
+ omits the 'extern' keyword:
+ * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]]
+ in both C and C++, there are two available positions:
+ - before the return type,
+ - between the function name and the parameter list.
+ * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax
+ in both C and C++, there are several available positions:
+ - before the return type,
+ - between return type and function name,
+ - at the end of the declaration.
+ * If _GL_ATTRIBUTE_* expands to bracket syntax [[...]] in C and to
+ __attribute__((...)) syntax in C++, there is one available position:
+ - before the return type.
+ * If _GL_ATTRIBUTE_* expands to __attribute__((...)) syntax in C and
+ to bracket syntax [[...]] in C++, there is one available position:
+ - before the return type.
+ The best choice is therefore to use the 'EXTERN_C_FUNC' idiom and
+ put the attributes before the return type. This works regardless
+ to what the _GL_ATTRIBUTE_* macros expand.
+ */
+
+/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...))
+ syntax, in static/inline function definitions.
+
+ There are similar constraints as for function declarations. However, here,
+ we cannot omit the storage-class specifier. Therefore, the following rule
+ applies:
+ * The macros
+ _GL_ATTRIBUTE_CONST
+ _GL_ATTRIBUTE_DEPRECATED
+ _GL_ATTRIBUTE_MAYBE_UNUSED
+ _GL_ATTRIBUTE_NODISCARD
+ _GL_ATTRIBUTE_PURE
+ _GL_ATTRIBUTE_REPRODUCIBLE
+ _GL_ATTRIBUTE_UNSEQUENCED
+ which may expand to bracket syntax [[...]], must come first, before the
+ storage-class specifier.
+ * Other _GL_ATTRIBUTE_* macros, that expand to __attribute__((...)) syntax,
+ are better placed between the storage-class specifier and the return
+ type.
+ */
+
+/* Attributes in bracket syntax [[...]] vs. attributes in __attribute__((...))
+ syntax, in variable declarations.
+
+ At which position can they be placed?
+ (Last tested with gcc/g++ 14 and clang/clang++ 18.)
+
+ =========================== foo.c = foo.cc ===========================
+ #ifdef __cplusplus
+ # define CC "C"
+ #else
+ # define CC
+ #endif
+
+ #define BD [[__deprecated__]]
+ #define AD __attribute__ ((__deprecated__))
+
+ // gcc clang g++ clang++
+
+ BD extern CC int var; // error error
+ extern CC BD int var; // error error
+ extern CC int BD var; // warn error warn error
+ extern CC int var BD;
+
+ AD extern CC int var; // warn
+ extern CC AD int var;
+ extern CC int AD var;
+ extern CC int var AD;
+
+ BD extern CC int z[]; // error error
+ extern CC BD int z[]; // error error
+ extern CC int BD z[]; // warn error warn error
+ extern CC int z1 BD [];
+ extern CC int z[] BD; // warn error error
+
+ AD extern CC int z[]; // warn
+ extern CC AD int z[];
+ extern CC int AD z[];
+ extern CC int z2 AD []; // error error error error
+ extern CC int z[] AD;
+ ======================================================================
+
+ * For non-array variables, the only good position is after the variable name,
+ that is, at the end of the declaration.
+ * For array variables, you will need to distinguish C and C++:
+ - In C, before the 'extern' keyword.
+ - In C++, between the 'extern "C"' and the variable's type.
+ */
+
+/* _GL_ATTRIBUTE_ALLOC_SIZE ((N)) declares that the Nth argument of the function
+ is the size of the returned memory block.
+ _GL_ATTRIBUTE_ALLOC_SIZE ((M, N)) declares that the Mth argument multiplied
+ by the Nth argument of the function is the size of the returned memory block.
+ */
+/* Applies to: functions, pointer to functions, function types. */
+#ifndef _GL_ATTRIBUTE_ALLOC_SIZE
+#if _GL_HAS_ATTRIBUTE(alloc_size)
+#define _GL_ATTRIBUTE_ALLOC_SIZE(args) __attribute__((__alloc_size__ args))
+#else
+#define _GL_ATTRIBUTE_ALLOC_SIZE(args)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_ALWAYS_INLINE tells that the compiler should always inline the
+ function and report an error if it cannot do so. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_ALWAYS_INLINE
+#if _GL_HAS_ATTRIBUTE(always_inline)
+#define _GL_ATTRIBUTE_ALWAYS_INLINE __attribute__((__always_inline__))
+#else
+#define _GL_ATTRIBUTE_ALWAYS_INLINE
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_ARTIFICIAL declares that the function is not important to show
+ in stack traces when debugging. The compiler should omit the function from
+ stack traces. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_ARTIFICIAL
+#if _GL_HAS_ATTRIBUTE(artificial)
+#define _GL_ATTRIBUTE_ARTIFICIAL __attribute__((__artificial__))
+#else
+#define _GL_ATTRIBUTE_ARTIFICIAL
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_COLD declares that the function is rarely executed. */
+/* Applies to: functions. */
+/* Avoid __attribute__ ((cold)) on MinGW; see thread starting at
+ <https://lists.gnu.org/r/emacs-devel/2019-04/msg01152.html>.
+ Also, Oracle Studio 12.6 requires 'cold' not '__cold__'. */
+#ifndef _GL_ATTRIBUTE_COLD
+#if _GL_HAS_ATTRIBUTE(cold) && !defined __MINGW32__
+#ifndef __SUNPRO_C
+#define _GL_ATTRIBUTE_COLD __attribute__((__cold__))
+#else
+#define _GL_ATTRIBUTE_COLD __attribute__((cold))
+#endif
+#else
+#define _GL_ATTRIBUTE_COLD
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_CONST declares:
+ It is OK for a compiler to move calls to the function and to omit
+ calls to the function if another call has the same arguments or the
+ result is not used.
+ This attribute is safe for a function that neither depends on
+ nor affects state, and always returns exactly once -
+ e.g., does not raise an exception, call longjmp, or loop forever.
+ (This attribute is stricter than _GL_ATTRIBUTE_PURE because the
+ function cannot observe state. It is stricter than
+ _GL_ATTRIBUTE_UNSEQUENCED because the function must return exactly
+ once and cannot depend on state addressed by its arguments.) */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_CONST
+#if _GL_HAS_ATTRIBUTE(const)
+#define _GL_ATTRIBUTE_CONST __attribute__((__const__))
+#else
+#define _GL_ATTRIBUTE_CONST _GL_ATTRIBUTE_UNSEQUENCED
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_DEALLOC (F, I) declares that the function returns pointers
+ that can be freed by passing them as the Ith argument to the
+ function F.
+ _GL_ATTRIBUTE_DEALLOC_FREE declares that the function returns pointers that
+ can be freed via 'free'; it can be used only after declaring 'free'. */
+/* Applies to: functions. Cannot be used on inline functions. */
+#ifndef _GL_ATTRIBUTE_DEALLOC
+#if _GL_GNUC_PREREQ(11, 0)
+#define _GL_ATTRIBUTE_DEALLOC(f, i) __attribute__((__malloc__(f, i)))
+#else
+#define _GL_ATTRIBUTE_DEALLOC(f, i)
+#endif
+#endif
+/* If gnulib's <string.h> or <wchar.h> has already defined this macro, continue
+ to use this earlier definition, since <stdlib.h> may not have been included
+ yet. */
+#ifndef _GL_ATTRIBUTE_DEALLOC_FREE
+#if defined __cplusplus && defined __GNUC__ && !defined __clang__
+/* Work around GCC bug <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=108231> */
+#define _GL_ATTRIBUTE_DEALLOC_FREE \
+ _GL_ATTRIBUTE_DEALLOC((void (*)(void*))free, 1)
+#else
+#define _GL_ATTRIBUTE_DEALLOC_FREE _GL_ATTRIBUTE_DEALLOC(free, 1)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_DEPRECATED: Declares that an entity is deprecated.
+ The compiler may warn if the entity is used. */
+/* Applies to:
+ - function, variable,
+ - struct, union, struct/union member,
+ - enumeration, enumeration item,
+ - typedef,
+ in C++ also: namespace, class, template specialization. */
+#ifndef _GL_ATTRIBUTE_DEPRECATED
+#ifndef _GL_BRACKET_BEFORE_ATTRIBUTE
+#if _GL_HAVE___HAS_C_ATTRIBUTE
+#if __has_c_attribute(__deprecated__)
+#define _GL_ATTRIBUTE_DEPRECATED [[__deprecated__]]
+#endif
+#endif
+#endif
+#if !defined _GL_ATTRIBUTE_DEPRECATED && _GL_HAS_ATTRIBUTE(deprecated)
+#define _GL_ATTRIBUTE_DEPRECATED __attribute__((__deprecated__))
+#endif
+#ifndef _GL_ATTRIBUTE_DEPRECATED
+#define _GL_ATTRIBUTE_DEPRECATED
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_ERROR(msg) requests an error if a function is called and
+ the function call is not optimized away.
+ _GL_ATTRIBUTE_WARNING(msg) requests a warning if a function is called and
+ the function call is not optimized away. */
+/* Applies to: functions. */
+#if !(defined _GL_ATTRIBUTE_ERROR && defined _GL_ATTRIBUTE_WARNING)
+#if _GL_HAS_ATTRIBUTE(error)
+#define _GL_ATTRIBUTE_ERROR(msg) __attribute__((__error__(msg)))
+#define _GL_ATTRIBUTE_WARNING(msg) __attribute__((__warning__(msg)))
+#elif _GL_HAS_ATTRIBUTE(diagnose_if)
+#define _GL_ATTRIBUTE_ERROR(msg) \
+ __attribute__((__diagnose_if__(1, msg, "error")))
+#define _GL_ATTRIBUTE_WARNING(msg) \
+ __attribute__((__diagnose_if__(1, msg, "warning")))
+#else
+#define _GL_ATTRIBUTE_ERROR(msg)
+#define _GL_ATTRIBUTE_WARNING(msg)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_EXTERNALLY_VISIBLE declares that the entity should remain
+ visible to debuggers etc., even with '-fwhole-program'. */
+/* Applies to: functions, variables. */
+#ifndef _GL_ATTRIBUTE_EXTERNALLY_VISIBLE
+#if _GL_HAS_ATTRIBUTE(externally_visible)
+#define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE __attribute__((externally_visible))
+#else
+#define _GL_ATTRIBUTE_EXTERNALLY_VISIBLE
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_FALLTHROUGH declares that it is not a programming mistake if
+ the control flow falls through to the immediately following 'case' or
+ 'default' label. The compiler should not warn in this case. */
+/* Applies to: Empty statement (;), inside a 'switch' statement. */
+/* Always expands to something. */
+#ifndef _GL_ATTRIBUTE_FALLTHROUGH
+#if _GL_HAVE___HAS_C_ATTRIBUTE
+#if __has_c_attribute(__fallthrough__)
+#define _GL_ATTRIBUTE_FALLTHROUGH [[__fallthrough__]]
+#endif
+#endif
+#if !defined _GL_ATTRIBUTE_FALLTHROUGH && _GL_HAS_ATTRIBUTE(fallthrough)
+#define _GL_ATTRIBUTE_FALLTHROUGH __attribute__((__fallthrough__))
+#endif
+#ifndef _GL_ATTRIBUTE_FALLTHROUGH
+#define _GL_ATTRIBUTE_FALLTHROUGH ((void)0)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_FORMAT ((ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK))
+ declares that the STRING-INDEXth function argument is a format string of
+ style ARCHETYPE, which is one of:
+ printf, gnu_printf
+ scanf, gnu_scanf,
+ strftime, gnu_strftime,
+ strfmon,
+ or the same thing prefixed and suffixed with '__'.
+ If FIRST-TO-CHECK is not 0, arguments starting at FIRST-TO_CHECK
+ are suitable for the format string. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_FORMAT
+#if _GL_HAS_ATTRIBUTE(format)
+#define _GL_ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
+#else
+#define _GL_ATTRIBUTE_FORMAT(spec)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_LEAF declares that if the function is called from some other
+ compilation unit, it executes code from that unit only by return or by
+ exception handling. This declaration lets the compiler optimize that unit
+ more aggressively. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_LEAF
+#if _GL_HAS_ATTRIBUTE(leaf)
+#define _GL_ATTRIBUTE_LEAF __attribute__((__leaf__))
+#else
+#define _GL_ATTRIBUTE_LEAF
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_MALLOC declares that the function returns a pointer to freshly
+ allocated memory. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_MALLOC
+#if _GL_HAS_ATTRIBUTE(malloc)
+#define _GL_ATTRIBUTE_MALLOC __attribute__((__malloc__))
+#else
+#define _GL_ATTRIBUTE_MALLOC
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_MAY_ALIAS declares that pointers to the type may point to the
+ same storage as pointers to other types. Thus this declaration disables
+ strict aliasing optimization. */
+/* Applies to: types. */
+/* Oracle Studio 12.6 mishandles may_alias despite __has_attribute OK. */
+#ifndef _GL_ATTRIBUTE_MAY_ALIAS
+#if _GL_HAS_ATTRIBUTE(may_alias) && !defined __SUNPRO_C
+#define _GL_ATTRIBUTE_MAY_ALIAS __attribute__((__may_alias__))
+#else
+#define _GL_ATTRIBUTE_MAY_ALIAS
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_MAYBE_UNUSED declares that it is not a programming mistake if
+ the entity is not used. The compiler should not warn if the entity is not
+ used. */
+/* Applies to:
+ - function, variable,
+ - struct, union, struct/union member,
+ - enumeration, enumeration item,
+ - typedef,
+ in C++ also: class. */
+/* In C++ and C23, this is spelled [[__maybe_unused__]].
+ GCC's syntax is __attribute__ ((__unused__)).
+ clang supports both syntaxes. Except that with clang ≥ 6, < 10, in C++ mode,
+ __has_c_attribute (__maybe_unused__) yields true but the use of
+ [[__maybe_unused__]] nevertheless produces a warning. */
+#ifndef _GL_ATTRIBUTE_MAYBE_UNUSED
+#ifndef _GL_BRACKET_BEFORE_ATTRIBUTE
+#if defined __clang__ && defined __cplusplus
+#if !defined __apple_build_version__ && __clang_major__ >= 10
+#define _GL_ATTRIBUTE_MAYBE_UNUSED [[__maybe_unused__]]
+#endif
+#elif _GL_HAVE___HAS_C_ATTRIBUTE
+#if __has_c_attribute(__maybe_unused__)
+#define _GL_ATTRIBUTE_MAYBE_UNUSED [[__maybe_unused__]]
+#endif
+#endif
+#endif
+#ifndef _GL_ATTRIBUTE_MAYBE_UNUSED
+#define _GL_ATTRIBUTE_MAYBE_UNUSED _GL_ATTRIBUTE_UNUSED
+#endif
+#endif
+/* Alternative spelling of this macro, for convenience and for
+ compatibility with glibc/include/libc-symbols.h. */
+#define _GL_UNUSED _GL_ATTRIBUTE_MAYBE_UNUSED
+/* Earlier spellings of this macro. */
+#define _UNUSED_PARAMETER_ _GL_ATTRIBUTE_MAYBE_UNUSED
+
+/* _GL_ATTRIBUTE_NODISCARD declares that the caller of the function should not
+ discard the return value. The compiler may warn if the caller does not use
+ the return value, unless the caller uses something like ignore_value. */
+/* Applies to: function, enumeration, class. */
+#ifndef _GL_ATTRIBUTE_NODISCARD
+#ifndef _GL_BRACKET_BEFORE_ATTRIBUTE
+#if defined __clang__ && defined __cplusplus
+/* With clang up to 15.0.6 (at least), in C++ mode, [[__nodiscard__]] produces
+ a warning.
+ The 1000 below means a yet unknown threshold. When clang++ version X
+ starts supporting [[__nodiscard__]] without warning about it, you can
+ replace the 1000 with X. */
+#if __clang_major__ >= 1000
+#define _GL_ATTRIBUTE_NODISCARD [[__nodiscard__]]
+#endif
+#elif _GL_HAVE___HAS_C_ATTRIBUTE
+#if __has_c_attribute(__nodiscard__)
+#define _GL_ATTRIBUTE_NODISCARD [[__nodiscard__]]
+#endif
+#endif
+#endif
+#if !defined _GL_ATTRIBUTE_NODISCARD && _GL_HAS_ATTRIBUTE(warn_unused_result)
+#define _GL_ATTRIBUTE_NODISCARD __attribute__((__warn_unused_result__))
+#endif
+#ifndef _GL_ATTRIBUTE_NODISCARD
+#define _GL_ATTRIBUTE_NODISCARD
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_NOINLINE tells that the compiler should not inline the
+ function. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_NOINLINE
+#if _GL_HAS_ATTRIBUTE(noinline)
+#define _GL_ATTRIBUTE_NOINLINE __attribute__((__noinline__))
+#else
+#define _GL_ATTRIBUTE_NOINLINE
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_NONNULL ((N1, N2,...)) declares that the arguments N1, N2,...
+ must not be NULL.
+ _GL_ATTRIBUTE_NONNULL () declares that all pointer arguments must not be
+ null. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_NONNULL
+#if _GL_HAS_ATTRIBUTE(nonnull)
+#define _GL_ATTRIBUTE_NONNULL(args) __attribute__((__nonnull__ args))
+#else
+#define _GL_ATTRIBUTE_NONNULL(args)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_NONSTRING declares that the contents of a character array is
+ not meant to be NUL-terminated. */
+/* Applies to: struct/union members and variables that are arrays of element
+ type '[[un]signed] char'. */
+#ifndef _GL_ATTRIBUTE_NONSTRING
+#if _GL_HAS_ATTRIBUTE(nonstring)
+#define _GL_ATTRIBUTE_NONSTRING __attribute__((__nonstring__))
+#else
+#define _GL_ATTRIBUTE_NONSTRING
+#endif
+#endif
+
+/* There is no _GL_ATTRIBUTE_NORETURN; use _Noreturn instead. */
+
+/* _GL_ATTRIBUTE_NOTHROW declares that the function does not throw exceptions.
+ */
+/* Applies to: functions. */
+/* After a function's parameter list, this attribute must come first, before
+ other attributes. */
+#ifndef _GL_ATTRIBUTE_NOTHROW
+#if defined __cplusplus
+#if _GL_GNUC_PREREQ(2, 8) || __clang_major__ >= 4
+#if __cplusplus >= 201103L
+#define _GL_ATTRIBUTE_NOTHROW noexcept(true)
+#else
+#define _GL_ATTRIBUTE_NOTHROW throw()
+#endif
+#else
+#define _GL_ATTRIBUTE_NOTHROW
+#endif
+#else
+#if _GL_HAS_ATTRIBUTE(nothrow)
+#define _GL_ATTRIBUTE_NOTHROW __attribute__((__nothrow__))
+#else
+#define _GL_ATTRIBUTE_NOTHROW
+#endif
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_PACKED declares:
+ For struct members: The member has the smallest possible alignment.
+ For struct, union, class: All members have the smallest possible alignment,
+ minimizing the memory required. */
+/* Applies to: struct members, struct, union,
+ in C++ also: class. */
+#ifndef _GL_ATTRIBUTE_PACKED
+/* Oracle Studio 12.6 miscompiles code with __attribute__ ((__packed__)) despite
+ __has_attribute OK. */
+#if _GL_HAS_ATTRIBUTE(packed) && !defined __SUNPRO_C
+#define _GL_ATTRIBUTE_PACKED __attribute__((__packed__))
+#else
+#define _GL_ATTRIBUTE_PACKED
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_PURE declares:
+ It is OK for a compiler to move calls to the function and to omit
+ calls to the function if another call has the same arguments or the
+ result is not used, and if observable state is the same.
+ This attribute is safe for a function that does not affect observable state
+ and always returns exactly once.
+ (This attribute is looser than _GL_ATTRIBUTE_CONST because the function
+ can depend on observable state. It is stricter than
+ _GL_ATTRIBUTE_REPRODUCIBLE because the function must return exactly
+ once and cannot affect state addressed by its arguments.) */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_PURE
+#if _GL_HAS_ATTRIBUTE(pure)
+#define _GL_ATTRIBUTE_PURE __attribute__((__pure__))
+#else
+#define _GL_ATTRIBUTE_PURE _GL_ATTRIBUTE_REPRODUCIBLE
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_REPRODUCIBLE declares:
+ It is OK for a compiler to move calls to the function and to omit duplicate
+ calls to the function with the same arguments, so long as the state
+ addressed by its arguments is the same and is updated in time for
+ the rest of the program.
+ This attribute is safe for a function that is effectless and idempotent; see
+ ISO C 23 § 6.7.12.7 for a definition of these terms.
+ (This attribute is looser than _GL_ATTRIBUTE_UNSEQUENCED because
+ the function need not be stateless and idempotent. It is looser
+ than _GL_ATTRIBUTE_PURE because the function need not return
+ exactly once and can affect state addressed by its arguments.)
+ See also <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2956.htm> and
+ <https://stackoverflow.com/questions/76847905/>.
+ ATTENTION! Efforts are underway to change the meaning of this attribute.
+ See <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3424.htm>. */
+/* Applies to: functions, pointer to functions, function types. */
+#ifndef _GL_ATTRIBUTE_REPRODUCIBLE
+/* This may be revisited when gcc and clang support [[reproducible]] or possibly
+ __attribute__ ((__reproducible__)). */
+#ifndef _GL_BRACKET_BEFORE_ATTRIBUTE
+#if _GL_HAS_ATTRIBUTE(reproducible)
+#define _GL_ATTRIBUTE_REPRODUCIBLE [[reproducible]]
+#endif
+#endif
+#ifndef _GL_ATTRIBUTE_REPRODUCIBLE
+#define _GL_ATTRIBUTE_REPRODUCIBLE
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_RETURNS_NONNULL declares that the function's return value is
+ a non-NULL pointer. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_RETURNS_NONNULL
+#if _GL_HAS_ATTRIBUTE(returns_nonnull)
+#define _GL_ATTRIBUTE_RETURNS_NONNULL __attribute__((__returns_nonnull__))
+#else
+#define _GL_ATTRIBUTE_RETURNS_NONNULL
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_SENTINEL(pos) declares that the variadic function expects a
+ trailing NULL argument.
+ _GL_ATTRIBUTE_SENTINEL () - The last argument is NULL (requires C99).
+ _GL_ATTRIBUTE_SENTINEL ((N)) - The (N+1)st argument from the end is NULL. */
+/* Applies to: functions. */
+#ifndef _GL_ATTRIBUTE_SENTINEL
+#if _GL_HAS_ATTRIBUTE(sentinel)
+#define _GL_ATTRIBUTE_SENTINEL(pos) __attribute__((__sentinel__ pos))
+#else
+#define _GL_ATTRIBUTE_SENTINEL(pos)
+#endif
+#endif
+
+/* _GL_ATTRIBUTE_UNSEQUENCED declares:
+ It is OK for a compiler to move calls to the function and to omit duplicate
+ calls to the function with the same arguments, so long as the state
+ addressed by its arguments is the same.
+ This attribute is safe for a function that is effectless, idempotent,
+ stateless, and independent; see ISO C 23 § 6.7.12.7 for a definition of
+ these terms.
+ (This attribute is stricter than _GL_ATTRIBUTE_REPRODUCIBLE because
+ the function must be stateless and independent. It is looser than
+ _GL_ATTRIBUTE_CONST because the function need not return exactly
+ once and can depend on state addressed by its arguments.)
+ See also <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n2956.htm> and
+ <https://stackoverflow.com/questions/76847905/>.
+ ATTENTION! Efforts are underway to change the meaning of this attribute.
+ See <https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3424.htm>. */
+/* Applies to: functions, pointer to functions, function types. */
+#ifndef _GL_ATTRIBUTE_UNSEQUENCED
+/* This may be revisited when gcc and clang support [[unsequenced]] or possibly
+ __attribute__ ((__unsequenced__)). */
+#ifndef _GL_BRACKET_BEFORE_ATTRIBUTE
+#if _GL_HAS_ATTRIBUTE(unsequenced)
+#define _GL_ATTRIBUTE_UNSEQUENCED [[unsequenced]]
+#endif
+#endif
+#ifndef _GL_ATTRIBUTE_UNSEQUENCED
+#define _GL_ATTRIBUTE_UNSEQUENCED
+#endif
+#endif
+
+/* A helper macro. Don't use it directly. */
+#ifndef _GL_ATTRIBUTE_UNUSED
+#if _GL_HAS_ATTRIBUTE(unused)
+#define _GL_ATTRIBUTE_UNUSED __attribute__((__unused__))
+#else
+#define _GL_ATTRIBUTE_UNUSED
+#endif
+#endif
+
+/* _GL_UNUSED_LABEL; declares that it is not a programming mistake if the
+ immediately preceding label is not used. The compiler should not warn
+ if the label is not used. */
+/* Applies to: label (both in C and C++). */
+/* Note that g++ < 4.5 does not support the '__attribute__ ((__unused__)) ;'
+ syntax. But clang does. */
+#ifndef _GL_UNUSED_LABEL
+#if !(defined __cplusplus && !_GL_GNUC_PREREQ(4, 5)) || defined __clang__
+#define _GL_UNUSED_LABEL _GL_ATTRIBUTE_UNUSED
+#else
+#define _GL_UNUSED_LABEL
+#endif
+#endif
+
+/* The following attributes enable detection of multithread-safety problems
+ and resource leaks at compile-time, by clang ≥ 15, when the warning option
+ -Wthread-safety is enabled. For usage, see
+ <https://clang.llvm.org/docs/ThreadSafetyAnalysis.html>. */
+#ifndef _GL_ATTRIBUTE_CAPABILITY_TYPE
+#if __clang_major__ >= 15
+#define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept) \
+ __attribute__((__capability__(concept)))
+#else
+#define _GL_ATTRIBUTE_CAPABILITY_TYPE(concept)
+#endif
+#endif
+#ifndef _GL_ATTRIBUTE_ACQUIRE_CAPABILITY
+#if __clang_major__ >= 15
+#define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource) \
+ __attribute__((__acquire_capability__(resource)))
+#else
+#define _GL_ATTRIBUTE_ACQUIRE_CAPABILITY(resource)
+#endif
+#endif
+#ifndef _GL_ATTRIBUTE_RELEASE_CAPABILITY
+#if __clang_major__ >= 15
+#define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource) \
+ __attribute__((__release_capability__(resource)))
+#else
+#define _GL_ATTRIBUTE_RELEASE_CAPABILITY(resource)
+#endif
+#endif
+
+/* In C++, there is the concept of "language linkage", that encompasses
+ name mangling and function calling conventions.
+ The following macros start and end a block of "C" linkage. */
+#ifdef __cplusplus
+#define _GL_BEGIN_C_LINKAGE extern "C" {
+#define _GL_END_C_LINKAGE }
+#else
+#define _GL_BEGIN_C_LINKAGE
+#define _GL_END_C_LINKAGE
+#endif
+
+/* Hidden symbol. */
+/* #undef frexp */
+
+/* Hidden symbol. */
+/* #undef frexpl */
+
+/* Define to '__inline__' or '__inline' if that's what the C compiler
+ calls it, or to nothing if 'inline' is not supported under any name. */
+#ifndef __cplusplus
+/* #undef inline */
+#endif
+
+/* Define to long or long long if <stdint.h> and <inttypes.h> don't define. */
+/* #undef intmax_t */
+
+/* Work around a bug in Apple GCC 4.0.1 build 5465: In C99 mode, it supports
+ the ISO C 99 semantics of 'extern inline' (unlike the GNU C semantics of
+ earlier versions), but does not display it by setting __GNUC_STDC_INLINE__.
+ __APPLE__ && __MACH__ test for Mac OS X.
+ __APPLE_CC__ tests for the Apple compiler and its version.
+ __STDC_VERSION__ tests for the C99 mode. */
+#if defined __APPLE__ && defined __MACH__ && __APPLE_CC__ >= 5465 && \
+ !defined __cplusplus && __STDC_VERSION__ >= 199901L && \
+ !defined __GNUC_STDC_INLINE__
+#define __GNUC_STDC_INLINE__ 1
+#endif
+
+/* Hidden symbol. */
+/* #undef mbrtowc */
+
+/* Hidden symbol. */
+/* #undef mbsinit */
+
+/* Define to a type if <wchar.h> does not define. */
+/* #undef mbstate_t */
+
+/* Hidden symbol. */
+/* #undef memchr */
+
+/* _GL_CMP (n1, n2) performs a three-valued comparison on n1 vs. n2, where
+ n1 and n2 are expressions without side effects, that evaluate to real
+ numbers (excluding NaN).
+ It returns
+ 1 if n1 > n2
+ 0 if n1 == n2
+ -1 if n1 < n2
+ The naïve code (n1 > n2 ? 1 : n1 < n2 ? -1 : 0) produces a conditional
+ jump with nearly all GCC versions up to GCC 10.
+ This variant (n1 < n2 ? -1 : n1 > n2) produces a conditional with many
+ GCC versions up to GCC 9.
+ The better code (n1 > n2) - (n1 < n2) from Hacker's Delight § 2-9
+ avoids conditional jumps in all GCC versions >= 3.4. */
+#define _GL_CMP(n1, n2) (((n1) > (n2)) - ((n1) < (n2)))
+
+/* Define to 'int' if <sys/types.h> does not define. */
+/* #undef mode_t */
+
+/* Define as a signed integer type capable of holding a process identifier. */
+/* #undef pid_t */
+
+/* Define as the type of the result of subtracting two pointers, if the system
+ doesn't define it. */
+/* #undef ptrdiff_t */
+
+/* Define to the equivalent of the C99 'restrict' keyword, or to
+ nothing if this is not supported. Do not define if restrict is
+ supported only directly. */
+#define restrict __restrict__
+/* Work around a bug in older versions of Sun C++, which did not
+ #define __restrict__ or support _Restrict or __restrict__
+ even though the corresponding Sun C compiler ended up with
+ "#define restrict _Restrict" or "#define restrict __restrict__"
+ in the previous line. This workaround can be removed once
+ we assume Oracle Developer Studio 12.5 (2016) or later. */
+#if defined __SUNPRO_CC && !defined __RESTRICT && !defined __restrict__
+#define _Restrict
+#define __restrict__
+#endif
+
+/* Hidden symbol. */
+/* #undef rpl_fgetc */
+
+/* Hidden symbol. */
+/* #undef rpl_fgets */
+
+/* Hidden symbol. */
+/* #undef rpl_fprintf */
+
+/* Hidden symbol. */
+/* #undef rpl_fputc */
+
+/* Hidden symbol. */
+/* #undef rpl_fputs */
+
+/* Hidden symbol. */
+/* #undef rpl_fread */
+
+/* Hidden symbol. */
+/* #undef rpl_frexp */
+
+/* Hidden symbol. */
+/* #undef rpl_frexpl */
+
+/* Hidden symbol. */
+/* #undef rpl_fscanf */
+
+/* Hidden symbol. */
+/* #undef rpl_fwrite */
+
+/* Hidden symbol. */
+/* #undef rpl_mbrtowc */
+
+/* Hidden symbol. */
+/* #undef rpl_mbsinit */
+
+/* Hidden symbol. */
+/* #undef rpl_memchr */
+
+/* Hidden symbol. */
+/* #undef rpl_tdelete */
+
+/* Hidden symbol. */
+/* #undef rpl_tfind */
+
+/* Hidden symbol. */
+/* #undef rpl_tsearch */
+
+/* Hidden symbol. */
+/* #undef rpl_twalk */
+
+/* Hidden symbol. */
+/* #undef rpl_vfprintf */
+
+/* Define as 'unsigned int' if <stddef.h> doesn't define. */
+/* #undef size_t */
+
+/* Define as a signed type of the same size as size_t. */
+/* #undef ssize_t */
+
+/* Hidden symbol. */
+/* #undef tdelete */
+
+/* Hidden symbol. */
+/* #undef tfind */
+
+/* Hidden symbol. */
+/* #undef tsearch */
+
+/* Hidden symbol. */
+/* #undef twalk */
+
+#define __libc_lock_t gl_lock_t
+#define __libc_lock_define gl_lock_define
+#define __libc_lock_define_initialized gl_lock_define_initialized
+#define __libc_lock_init gl_lock_init
+#define __libc_lock_lock gl_lock_lock
+#define __libc_lock_unlock gl_lock_unlock
+#define __libc_lock_recursive_t gl_recursive_lock_t
+#define __libc_lock_define_recursive gl_recursive_lock_define
+#define __libc_lock_define_initialized_recursive \
+ gl_recursive_lock_define_initialized
+#define __libc_lock_init_recursive gl_recursive_lock_init
+#define __libc_lock_lock_recursive gl_recursive_lock_lock
+#define __libc_lock_unlock_recursive gl_recursive_lock_unlock
+
+#define asnprintf _libintl_asnprintf
+#define rpl_asnprintf _libintl_asnprintf
+/* Symbols defined by main intl code. The prefix '_nl_' is used by glibc.
+ For hiding the symbols on AIX and Solaris 10 with compilers that don't
+ support the __visibility__ attribute, map them to prefix '_libintl_'. */
+#define _nl_explode_name _libintl_explode_name
+#define _nl_find_domain _libintl_find_domain
+#define _nl_find_msg _libintl_find_msg
+#define _nl_language_preferences_default _libintl_language_preferences_default
+#define _nl_load_domain _libintl_load_domain
+#define _nl_log_untranslated _libintl_log_untranslated
+#define _nl_make_l10nflist _libintl_make_l10nflist
+#define _nl_normalize_codeset _libintl_normalize_codeset
+#define _nl_state_lock _libintl_state_lock
+/* Symbols defined by gnulib module 'float'. */
+#define gl_LDBL_MAX _libintl_LDBL_MAX
+/* Symbols defined by gnulib module 'free-posix'. */
+#define rpl_free _libintl_free
+/* Symbols defined by gnulib module 'hard-locale'. */
+#define hard_locale _libintl_hard_locale
+/* Symbols defined by gnulib module 'isnand-nolibm'. */
+#define rpl_isnand _libintl_isnand
+/* Symbols defined by gnulib module 'isnanf-nolibm'. */
+#define rpl_isnanf _libintl_isnanf
+/* Symbols defined by gnulib module 'isnanl-nolibm'. */
+#define rpl_isnanl _libintl_isnanl
+/* Symbols defined by gnulib module 'localename'. */
+#define gl_locale_name_thread _libintl_locale_name_thread
+#define gl_locale_name_posix _libintl_locale_name_posix
+#define gl_locale_name _libintl_locale_name
+/* Symbols defined by gnulib module 'localename-unsafe'. */
+#define gl_locale_name_canonicalize _libintl_locale_name_canonicalize
+#define gl_locale_name_from_win32_LANGID _libintl_locale_name_from_win32_LANGID
+#define gl_locale_name_from_win32_LCID _libintl_locale_name_from_win32_LCID
+#define gl_locale_name_thread_unsafe _libintl_locale_name_thread_unsafe
+#define gl_locale_name_posix_unsafe _libintl_locale_name_posix_unsafe
+#define gl_locale_name_environ _libintl_locale_name_environ
+#define gl_locale_name_default _libintl_locale_name_default
+#define gl_locale_name_unsafe _libintl_locale_name_unsafe
+#define rpl_newlocale _libintl_newlocale
+#define rpl_duplocale _libintl_duplocale
+#define rpl_freelocale _libintl_freelocale
+/* Symbols defined by gnulib module 'lock'. */
+#if USE_ISOC_THREADS || USE_ISOC_AND_POSIX_THREADS
+#define glthread_lock_init _libintl_lock_init
+#define glthread_lock_lock _libintl_lock_lock
+#define glthread_lock_unlock _libintl_lock_unlock
+#define glthread_lock_destroy _libintl_lock_destroy
+#define glthread_rwlock_init _libintl_rwlock_init
+#define glthread_rwlock_rdlock _libintl_rwlock_rdlock
+#define glthread_rwlock_wrlock _libintl_rwlock_wrlock
+#define glthread_rwlock_unlock _libintl_rwlock_unlock
+#define glthread_rwlock_destroy _libintl_rwlock_destroy
+#define glthread_recursive_lock_init _libintl_recursive_lock_init
+#define glthread_recursive_lock_lock _libintl_recursive_lock_lock
+#define glthread_recursive_lock_unlock _libintl_recursive_lock_unlock
+#define glthread_recursive_lock_destroy _libintl_recursive_lock_destroy
+#endif
+#define glthread_rwlock_init_for_glibc _libintl_rwlock_init_for_glibc
+#define glthread_rwlock_init_multithreaded _libintl_rwlock_init_multithreaded
+#define glthread_rwlock_rdlock_multithreaded \
+ _libintl_rwlock_rdlock_multithreaded
+#define glthread_rwlock_wrlock_multithreaded \
+ _libintl_rwlock_wrlock_multithreaded
+#define glthread_rwlock_unlock_multithreaded \
+ _libintl_rwlock_unlock_multithreaded
+#define glthread_rwlock_destroy_multithreaded \
+ _libintl_rwlock_destroy_multithreaded
+#define glthread_recursive_lock_init_multithreaded \
+ _libintl_recursive_lock_init_multithreaded
+#define glthread_recursive_lock_lock_multithreaded \
+ _libintl_recursive_lock_lock_multithreaded
+#define glthread_recursive_lock_unlock_multithreaded \
+ _libintl_recursive_lock_unlock_multithreaded
+#define glthread_recursive_lock_destroy_multithreaded \
+ _libintl_recursive_lock_destroy_multithreaded
+#define glthread_once_singlethreaded _libintl_once_singlethreaded
+#define glthread_once_multithreaded _libintl_once_multithreaded
+/* Symbols defined by gnulib module 'mbszero'. */
+#define mbszero _libintl_mbszero
+/* Symbols defined by gnulib module 'printf-frexp'. */
+#define printf_frexp _libintl_printf_frexp
+/* Symbols defined by gnulib module 'printf-frexpl'. */
+#define printf_frexpl _libintl_printf_frexpl
+/* Symbols defined by gnulib module 'setlocale-null'. */
+#define setlocale_null _libintl_setlocale_null
+#define setlocale_null_r _libintl_setlocale_null_r
+/* Symbols defined by gnulib module 'setlocale-null-unlocked'. */
+#define setlocale_null_unlocked _libintl_setlocale_null_unlocked
+#define setlocale_null_r_unlocked _libintl_setlocale_null_r_unlocked
+/* Symbols defined by gnulib module 'signbit'. */
+#define gl_signbitf _libintl_signbitf
+#define gl_signbitd _libintl_signbitd
+#define gl_signbitl _libintl_signbitl
+/* Symbols defined by gnulib module 'threadlib'. */
+#define glthread_in_use _libintl_glthread_in_use
+/* Symbols defined by gnulib module 'vasnprintf'. */
+#define printf_fetchargs _libintl_printf_fetchargs
+#define printf_parse _libintl_printf_parse
+#define vasnprintf _libintl_vasnprintf
+#define rpl_vasnprintf _libintl_vasnprintf
+/* Symbols defined by gnulib module 'vasnwprintf'. */
+#define asnwprintf _libintl_asnwprintf
+#define wprintf_parse _libintl_wprintf_parse
+#define vasnwprintf _libintl_vasnwprintf
+/* Symbols defined by gnulib module 'windows-mutex'. */
+#define glwthread_mutex_init _libintl_glwthread_mutex_init
+#define glwthread_mutex_lock _libintl_glwthread_mutex_lock
+#define glwthread_mutex_trylock _libintl_glwthread_mutex_trylock
+#define glwthread_mutex_unlock _libintl_glwthread_mutex_unlock
+#define glwthread_mutex_destroy _libintl_glwthread_mutex_destroy
+/* Symbols defined by gnulib module 'windows-once'. */
+#define glwthread_once _libintl_glwthread_once
+/* Symbols defined by gnulib module 'windows-recmutex'. */
+#define glwthread_recmutex_init _libintl_glwthread_recmutex_init
+#define glwthread_recmutex_lock _libintl_glwthread_recmutex_lock
+#define glwthread_recmutex_trylock _libintl_glwthread_recmutex_trylock
+#define glwthread_recmutex_unlock _libintl_glwthread_recmutex_unlock
+#define glwthread_recmutex_destroy _libintl_glwthread_recmutex_destroy
+/* Symbols defined by gnulib module 'windows-rwlock'. */
+#define glwthread_rwlock_init _libintl_glwthread_rwlock_init
+#define glwthread_rwlock_rdlock _libintl_glwthread_rwlock_rdlock
+#define glwthread_rwlock_wrlock _libintl_glwthread_rwlock_wrlock
+#define glwthread_rwlock_tryrdlock _libintl_glwthread_rwlock_tryrdlock
+#define glwthread_rwlock_trywrlock _libintl_glwthread_rwlock_trywrlock
+#define glwthread_rwlock_unlock _libintl_glwthread_rwlock_unlock
+#define glwthread_rwlock_destroy _libintl_glwthread_rwlock_destroy
+/* Symbols defined by gnulib module 'xsize'. */
+#define xmax _libintl_xmax
+#define xsum _libintl_xsum
+#define xsum3 _libintl_xsum3
+#define xsum4 _libintl_xsum4
+
+#if !(defined __cplusplus \
+ ? 1 \
+ : (defined __clang__ \
+ ? __STDC_VERSION__ >= 202000L && __clang_major__ >= 15 \
+ : (defined __GNUC__ \
+ ? __STDC_VERSION__ >= 202000L && __GNUC__ >= 13 \
+ : defined HAVE_C_BOOL)))
+#if !defined __cplusplus && !defined __bool_true_false_are_defined
+#if HAVE_STDBOOL_H
+#include <stdbool.h>
+#else
+#if defined __SUNPRO_C
+#error \
+ "<stdbool.h> is not usable with this configuration. To make it usable, add -D_STDC_C99= to $CC."
+#else
+#error \
+ "<stdbool.h> does not exist on this platform. Use gnulib module 'stdbool-c99' instead of gnulib module 'stdbool'."
+#endif
+#endif
+#endif
+#if !true
+#define true (!false)
+#endif
+#endif
+
+#if (!(defined __clang__ \
+ ? (defined __cplusplus \
+ ? __cplusplus >= 201703L \
+ : __STDC_VERSION__ >= 202000L && __clang_major__ >= 16 && \
+ !defined __sun) \
+ : (defined __GNUC__ \
+ ? (defined __cplusplus \
+ ? __cplusplus >= 201103L && __GNUG__ >= 6 \
+ : __STDC_VERSION__ >= 202000L && __GNUC__ >= 13 && \
+ !defined __sun) \
+ : defined HAVE_C_STATIC_ASSERT)) && \
+ !defined assert && \
+ (!defined __cplusplus || \
+ (__cpp_static_assert < 201411 && __GNUG__ < 6 && __clang_major__ < 6)))
+#include <assert.h>
+#undef /**/ assert
+#ifdef __sgi
+#undef /**/ __ASSERT_H__
+#endif
+/* Solaris 11.4 <assert.h> defines static_assert as a macro with 2 arguments.
+ We need it also to be invocable with a single argument.
+ Haiku 2022 <assert.h> does not define static_assert at all. */
+#if (__STDC_VERSION__ - 0 >= 201112L) && !defined __cplusplus
+#undef /**/ static_assert
+#define static_assert _Static_assert
+#endif
+#endif
+
+/* Tweak gnulib code according to the needs of this library. */
+#define IN_LIBINTL 1
+
+/* On Windows, variables that may be in a DLL must be marked specially.
+ The symbols marked with DLL_VARIABLE should be exported if and only if the
+ object file gets included in a DLL. Libtool, on Windows platforms, defines
+ the C macro DLL_EXPORT (together with PIC) when compiling for a shared
+ library (called DLL under Windows) and does not define it when compiling
+ an object file meant to be linked statically into some executable. */
+#if (defined _MSC_VER && defined DLL_EXPORT) && !defined IN_RELOCWRAPPER
+#define DLL_VARIABLE __declspec(dllimport)
+#else
+#define DLL_VARIABLE
+#endif
+
+/* Extra OS/2 (emx+gcc) defines. */
+#if defined __EMX__ && !defined __KLIBC__
+#include "os2compat.h"
+#endif
+
+/* ADDED FOR GHOSTTY. This is needed to ensure that all gnulib-lib
+ * source files have the locale_t type and all the LC_ constants.
+ * It looks like some files do this and some don't so we just put it
+ * here to ensure we always have it. This surely can't be the best or
+ * correct way to do this but it works.
+ *
+ * When upgrading gettext we just need to be careful about this. This
+ * is noted in the build.zig as well.
+ */
+#if HAVE_GOOD_USELOCALE
+/* Mac OS X 10.5 defines the locale_t type in <xlocale.h>. */
+#if defined __APPLE__ && defined __MACH__
+#include <xlocale.h>
+#endif
+#endif
diff --git a/vendor/libghostty-vt/pkg/libintl/libgnuintl.h b/vendor/libghostty-vt/pkg/libintl/libgnuintl.h
new file mode 100644
index 00000000..c972210f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libintl/libgnuintl.h
@@ -0,0 +1 @@
+#include "libintl.h"
diff --git a/vendor/libghostty-vt/pkg/libintl/libintl.h b/vendor/libghostty-vt/pkg/libintl/libintl.h
new file mode 100644
index 00000000..f4930181
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libintl/libintl.h
@@ -0,0 +1,1168 @@
+/* Message catalogs for internationalization.
+ Copyright (C) 1995-2025 Free Software Foundation, Inc.
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 2.1 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with this program. If not, see <https://www.gnu.org/licenses/>. */
+
+#ifndef _LIBINTL_H
+#define _LIBINTL_H 1
+
+#include <locale.h>
+#if (defined __APPLE__ && defined __MACH__) && 1
+#include <xlocale.h>
+#endif
+
+/* The LC_MESSAGES locale category is the category used by the functions
+ gettext() and dgettext(). It is specified in POSIX, but not in ANSI C.
+ On systems that don't define it, use an arbitrary value instead.
+ On Solaris, <locale.h> defines __LOCALE_H (or _LOCALE_H in Solaris 2.5)
+ then includes <libintl.h> (i.e. this file!) and then only defines
+ LC_MESSAGES. To avoid a redefinition warning, don't define LC_MESSAGES
+ in this case. */
+#if !defined LC_MESSAGES && \
+ !(defined __LOCALE_H || (defined _LOCALE_H && defined __sun))
+#define LC_MESSAGES 1729
+#endif
+
+/* We define an additional symbol to signal that we use the GNU
+ implementation of gettext. */
+#define __USE_GNU_GETTEXT 1
+
+/* Provide information about the supported file formats. Returns the
+ maximum minor revision number supported for a given major revision. */
+#define __GNU_GETTEXT_SUPPORTED_REVISION(major) \
+ ((major) == 0 || (major) == 1 ? 1 : -1)
+
+/* Resolve a platform specific conflict on DJGPP. GNU gettext takes
+ precedence over _conio_gettext. */
+#ifdef __DJGPP__
+#undef gettext
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Version number: (major<<16) + (minor<<8) + subminor */
+#define LIBINTL_VERSION 0x001800
+extern int libintl_version;
+
+/* We redirect the functions to those prefixed with "libintl_". This is
+ necessary, because some systems define gettext/textdomain/... in the C
+ library (namely, Solaris 2.4 and newer, and GNU libc 2.0 and newer).
+ If we used the unprefixed names, there would be cases where the
+ definition in the C library would override the one in the libintl.so
+ shared library. Recall that on ELF systems, the symbols are looked
+ up in the following order:
+ 1. in the executable,
+ 2. in the shared libraries specified on the link command line, in order,
+ 3. in the dependencies of the shared libraries specified on the link
+ command line,
+ 4. in the dlopen()ed shared libraries, in the order in which they were
+ dlopen()ed.
+ The definition in the C library would override the one in libintl.so if
+ either
+ * -lc is given on the link command line and -lintl isn't, or
+ * -lc is given on the link command line before -lintl, or
+ * libintl.so is a dependency of a dlopen()ed shared library but not
+ linked to the executable at link time.
+ Since Solaris gettext() behaves differently than GNU gettext(), this
+ would be unacceptable.
+
+ For the redirection, three mechanisms are available:
+ * _INTL_REDIRECT_ASM uses a function declaration with 'asm', that
+ specifies a different symbol at the linker level than at the C level.
+ * _INTL_REDIRECT_INLINE uses an inline function definition. In C,
+ we use 'static inline' to force the override. In C++, it is better
+ to use 'inline' without 'static'. But since the override is only
+ effective if the inlining happens, we need to use
+ __attribute__ ((__always_inline__)), which is supported in g++ >= 3.1
+ and clang. MSVC has a similar keyword __forceinline (see
+ <https://learn.microsoft.com/en-us/cpp/cpp/inline-functions-cpp>),
+ but it has an effect only when optimizing is enabled, and there is no
+ preprocessor macro that tells us whether optimizing is enabled.
+ * _INTL_REDIRECT_MACROS uses C macros.
+ The drawbacks are:
+ * _INTL_REDIRECT_ASM and _INTL_REDIRECT_INLINE don't work when the
+ function has an inline function definition in a system header file;
+ this mostly affects mingw and MSVC. In these cases,
+ _INTL_REDIRECT_MACROS is the only mechanism that works.
+ * _INTL_REDIRECT_MACROS can interfere with symbols used in structs and
+ classes (especially in C++, but also in C). For example, Qt has a class
+ with an 'asprintf' member, and our '#define asprintf libintl_asprintf'
+ triggers a compilation error.
+ * _INTL_REDIRECT_INLINE in C mode has the effect that each function's
+ address, such as &gettext, is different in each compilation unit.
+ */
+
+/* _INTL_FORCE_INLINE ensures inlining of a function, even when not
+ optimizing. */
+/* Applies to: functions. */
+/* Supported by g++ >= 3.1 and clang. Actually needed for g++ < 4.0. */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 1) > 3) || \
+ defined __clang__
+#define _INTL_HAS_FORCE_INLINE
+#define _INTL_FORCE_INLINE __attribute__((__always_inline__))
+#else
+#define _INTL_FORCE_INLINE
+#endif
+
+/* The user can define _INTL_REDIRECT_INLINE or _INTL_REDIRECT_MACROS.
+ If he doesn't, we choose the method. */
+#if !(defined _INTL_REDIRECT_INLINE || defined _INTL_REDIRECT_MACROS)
+#if defined __GNUC__ && __GNUC__ >= 2 && \
+ !(defined __APPLE_CC__ && __APPLE_CC__ > 1) && !defined __MINGW32__ && \
+ !(__GNUC__ == 2 && defined _AIX) && \
+ (defined __STDC__ || defined __cplusplus)
+#define _INTL_REDIRECT_ASM
+#else
+#if defined __cplusplus && defined _INTL_HAS_FORCE_INLINE
+#define _INTL_REDIRECT_INLINE
+#else
+#define _INTL_REDIRECT_MACROS
+#endif
+#endif
+#endif
+/* Auxiliary macros. */
+#ifdef _INTL_REDIRECT_ASM
+#define _INTL_ASM(cname) __asm__(_INTL_ASMNAME(__USER_LABEL_PREFIX__, #cname))
+#define _INTL_ASMNAME(prefix, cnamestring) _INTL_STRINGIFY(prefix) cnamestring
+#define _INTL_STRINGIFY(prefix) #prefix
+#else
+#define _INTL_ASM(cname)
+#endif
+
+/* _INTL_MAY_RETURN_STRING_ARG(n) declares that the given function may return
+ its n-th argument literally. This enables GCC to warn for example about
+ printf (gettext ("foo %y")). */
+#if ((defined __GNUC__ && __GNUC__ >= 3) || defined __clang__) && \
+ !(defined __APPLE_CC__ && __APPLE_CC__ > 1 && \
+ !(defined __clang__ && __clang__ && __clang_major__ >= 3) && \
+ defined __cplusplus)
+#define _INTL_MAY_RETURN_STRING_ARG(n) __attribute__((__format_arg__(n)))
+#else
+#define _INTL_MAY_RETURN_STRING_ARG(n)
+#endif
+
+/* _INTL_ATTRIBUTE_FORMAT ((ARCHETYPE, STRING-INDEX, FIRST-TO-CHECK))
+ declares that the STRING-INDEXth function argument is a format string of
+ style ARCHETYPE, which is one of:
+ printf, gnu_printf
+ scanf, gnu_scanf,
+ strftime, gnu_strftime,
+ strfmon,
+ or the same thing prefixed and suffixed with '__'.
+ If FIRST-TO-CHECK is not 0, arguments starting at FIRST-TO_CHECK
+ are suitable for the format string. */
+/* Applies to: functions. */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 7) > 2) || \
+ defined __clang__
+#define _INTL_ATTRIBUTE_FORMAT(spec) __attribute__((__format__ spec))
+#else
+#define _INTL_ATTRIBUTE_FORMAT(spec)
+#endif
+
+/* _INTL_ATTRIBUTE_SPEC_PRINTF_STANDARD
+ An __attribute__ __format__ specifier for a function that takes a format
+ string and arguments, where the format string directives are the ones
+ standardized by ISO C99 and POSIX. */
+/* __gnu_printf__ is supported in GCC >= 4.4. */
+#if defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 4) > 4
+#define _INTL_ATTRIBUTE_SPEC_PRINTF_STANDARD __gnu_printf__
+#else
+#define _INTL_ATTRIBUTE_SPEC_PRINTF_STANDARD __printf__
+#endif
+
+/* _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD
+ indicates to GCC that the function takes a format string and arguments,
+ where the format string directives are the ones standardized by ISO C99
+ and POSIX. */
+#define _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(formatstring_parameter, \
+ first_argument) \
+ _INTL_ATTRIBUTE_FORMAT((_INTL_ATTRIBUTE_SPEC_PRINTF_STANDARD, \
+ formatstring_parameter, first_argument))
+
+/* _INTL_ARG_NONNULL ((N1, N2,...)) declares that the arguments N1, N2,...
+ must not be NULL. */
+/* Applies to: functions. */
+#if (defined __GNUC__ && __GNUC__ + (__GNUC_MINOR__ >= 3) > 3) || \
+ defined __clang__
+#define _INTL_ARG_NONNULL(params) __attribute__((__nonnull__ params))
+#else
+#define _INTL_ARG_NONNULL(params)
+#endif
+
+/* Look up MSGID in the current default message catalog for the current
+ LC_MESSAGES locale. If not found, returns MSGID itself (the default
+ text). */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_gettext(const char* __msgid)
+ _INTL_MAY_RETURN_STRING_ARG(1);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(1) char* gettext(const char* __msgid) {
+ return libintl_gettext(__msgid);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define gettext libintl_gettext
+#endif
+extern char* gettext(const char* __msgid) _INTL_ASM(libintl_gettext)
+ _INTL_MAY_RETURN_STRING_ARG(1);
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current
+ LC_MESSAGES locale. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_dgettext(const char* __domainname, const char* __msgid)
+ _INTL_MAY_RETURN_STRING_ARG(2);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(2) char* dgettext(const char* __domainname,
+ const char* __msgid) {
+ return libintl_dgettext(__domainname, __msgid);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define dgettext libintl_dgettext
+#endif
+extern char* dgettext(const char* __domainname, const char* __msgid)
+ _INTL_ASM(libintl_dgettext) _INTL_MAY_RETURN_STRING_ARG(2);
+#endif
+
+/* Look up MSGID in the DOMAINNAME message catalog for the current CATEGORY
+ locale. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_dcgettext(const char* __domainname,
+ const char* __msgid,
+ int __category) _INTL_MAY_RETURN_STRING_ARG(2);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(2) char* dcgettext(const char* __domainname,
+ const char* __msgid,
+ int __category) {
+ return libintl_dcgettext(__domainname, __msgid, __category);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define dcgettext libintl_dcgettext
+#endif
+extern char* dcgettext(const char* __domainname,
+ const char* __msgid,
+ int __category) _INTL_ASM(libintl_dcgettext)
+ _INTL_MAY_RETURN_STRING_ARG(2);
+#endif
+
+/* Similar to 'gettext' but select the plural form corresponding to the
+ number N. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_ngettext(const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n)
+ _INTL_MAY_RETURN_STRING_ARG(1) _INTL_MAY_RETURN_STRING_ARG(2);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(1)
+ _INTL_MAY_RETURN_STRING_ARG(2) char* ngettext(const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n) {
+ return libintl_ngettext(__msgid1, __msgid2, __n);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define ngettext libintl_ngettext
+#endif
+extern char* ngettext(const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n) _INTL_ASM(libintl_ngettext)
+ _INTL_MAY_RETURN_STRING_ARG(1) _INTL_MAY_RETURN_STRING_ARG(2);
+#endif
+
+/* Similar to 'dgettext' but select the plural form corresponding to the
+ number N. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_dngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n)
+ _INTL_MAY_RETURN_STRING_ARG(2) _INTL_MAY_RETURN_STRING_ARG(3);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(2)
+ _INTL_MAY_RETURN_STRING_ARG(3) char* dngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n) {
+ return libintl_dngettext(__domainname, __msgid1, __msgid2, __n);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define dngettext libintl_dngettext
+#endif
+extern char* dngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n) _INTL_ASM(libintl_dngettext)
+ _INTL_MAY_RETURN_STRING_ARG(2) _INTL_MAY_RETURN_STRING_ARG(3);
+#endif
+
+/* Similar to 'dcgettext' but select the plural form corresponding to the
+ number N. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_dcngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n,
+ int __category) _INTL_MAY_RETURN_STRING_ARG(2)
+ _INTL_MAY_RETURN_STRING_ARG(3);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_MAY_RETURN_STRING_ARG(2) _INTL_MAY_RETURN_STRING_ARG(
+ 3) char* dcngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n,
+ int __category) {
+ return libintl_dcngettext(__domainname, __msgid1, __msgid2, __n, __category);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define dcngettext libintl_dcngettext
+#endif
+extern char* dcngettext(const char* __domainname,
+ const char* __msgid1,
+ const char* __msgid2,
+ unsigned long int __n,
+ int __category) _INTL_ASM(libintl_dcngettext)
+ _INTL_MAY_RETURN_STRING_ARG(2) _INTL_MAY_RETURN_STRING_ARG(3);
+#endif
+
+/* Set the current default message catalog to DOMAINNAME.
+ If DOMAINNAME is null, return the current default.
+ If DOMAINNAME is "", reset to the default of "messages". */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_textdomain(const char* __domainname);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE char*
+ textdomain(const char* __domainname) {
+ return libintl_textdomain(__domainname);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define textdomain libintl_textdomain
+#endif
+extern char* textdomain(const char* __domainname) _INTL_ASM(libintl_textdomain);
+#endif
+
+/* Specify that the DOMAINNAME message catalog will be found
+ in DIRNAME rather than in the system locale data base. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_bindtextdomain(const char* __domainname,
+ const char* __dirname);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE char*
+ bindtextdomain(const char* __domainname, const char* __dirname) {
+ return libintl_bindtextdomain(__domainname, __dirname);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define bindtextdomain libintl_bindtextdomain
+#endif
+extern char* bindtextdomain(const char* __domainname, const char* __dirname)
+ _INTL_ASM(libintl_bindtextdomain);
+#endif
+
+#if defined _WIN32 && !defined __CYGWIN__
+/* Specify that the DOMAINNAME message catalog will be found
+ in WDIRNAME rather than in the system locale data base. */
+#ifdef _INTL_REDIRECT_INLINE
+extern wchar_t* libintl_wbindtextdomain(const char* __domainname,
+ const wchar_t* __wdirname);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE wchar_t*
+ wbindtextdomain(const char* __domainname, const wchar_t* __wdirname) {
+ return libintl_wbindtextdomain(__domainname, __wdirname);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define wbindtextdomain libintl_wbindtextdomain
+#endif
+extern wchar_t* wbindtextdomain(const char* __domainname,
+ const wchar_t* __wdirname)
+ _INTL_ASM(libintl_wbindtextdomain);
+#endif
+#endif
+
+/* Specify the character encoding in which the messages from the
+ DOMAINNAME message catalog will be returned. */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_bind_textdomain_codeset(const char* __domainname,
+ const char* __codeset);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE char*
+ bind_textdomain_codeset(const char* __domainname, const char* __codeset) {
+ return libintl_bind_textdomain_codeset(__domainname, __codeset);
+}
+#else
+#ifdef _INTL_REDIRECT_MACROS
+#define bind_textdomain_codeset libintl_bind_textdomain_codeset
+#endif
+extern char* bind_textdomain_codeset(const char* __domainname,
+ const char* __codeset)
+ _INTL_ASM(libintl_bind_textdomain_codeset);
+#endif
+
+/* Support for format strings with positions in *printf(), following the
+ POSIX/XSI specification.
+ Note: These replacements for the *printf() functions are visible only
+ in source files that #include <libintl.h> or #include "gettext.h".
+ Packages that use *printf() in source files that don't refer to _()
+ or gettext() but for which the format string could be the return value
+ of _() or gettext() need to add this #include. Oh well. */
+
+/* Note: In C++ mode, it is not sufficient to redefine a symbol at the
+ preprocessor macro level, such as
+ #define sprintf libintl_sprintf
+ Some programs may reference std::sprintf after including <libintl.h>.
+ Therefore we must make sure that std::libintl_sprintf is defined and
+ identical to ::libintl_sprintf.
+ The user can define _INTL_CXX_NO_CLOBBER_STD_NAMESPACE to avoid this.
+ In such cases, they will not benefit from the overrides when using
+ the 'std' namespace, and they will need to do the references to the
+ 'std' namespace *before* including <libintl.h> or "gettext.h". */
+
+#if !1
+
+#include <stddef.h>
+#include <stdio.h>
+
+/* Get va_list. */
+#if (defined __STDC__ && __STDC__) || defined __cplusplus || defined _MSC_VER
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if !((defined fprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_fprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vfprintf(FILE*, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3)
+ _INTL_ARG_NONNULL((1, 2)) int fprintf(FILE* __stream,
+ const char* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vfprintf(__stream, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_FPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef fprintf
+#define fprintf libintl_fprintf
+#endif
+extern int fprintf(FILE*, const char*, ...) _INTL_ASM(libintl_fprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_fprintf;
+}
+#endif
+#endif
+#endif
+#if !((defined vfprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_vfprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vfprintf(FILE*, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0)
+ _INTL_ARG_NONNULL((1, 2)) int vfprintf(FILE* __stream,
+ const char* __format,
+ va_list __args) {
+ return libintl_vfprintf(__stream, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VFPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef vfprintf
+#define vfprintf libintl_vfprintf
+#endif
+extern int vfprintf(FILE*, const char*, va_list) _INTL_ASM(libintl_vfprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vfprintf;
+}
+#endif
+#endif
+#endif
+
+#if !((defined printf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_printf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vprintf(const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 0) _INTL_ARG_NONNULL((1));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 2)
+ _INTL_ARG_NONNULL((1)) int printf(const char* __format, ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vprintf(__format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_PRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef printf
+#if defined __NetBSD__ || defined __BEOS__ || defined __CYGWIN__ || \
+ defined __MINGW32__
+/* Don't break __attribute__((format(printf,M,N))).
+ This redefinition is only possible because the libc in NetBSD, Cygwin,
+ mingw does not have a function __printf__.
+ Alternatively, we could have done this redirection only when compiling with
+ __GNUC__, together with a symbol redirection:
+ extern int printf (const char *, ...)
+ __asm__ (#__USER_LABEL_PREFIX__ "libintl_printf");
+ But doing it now would introduce a binary incompatibility with already
+ distributed versions of libintl on these systems. */
+#define libintl_printf __printf__
+#endif
+#define printf libintl_printf
+#endif
+extern int printf(const char*, ...) _INTL_ASM(libintl_printf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 2) _INTL_ARG_NONNULL((1));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_printf;
+}
+#endif
+#endif
+#endif
+#if !((defined vprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_vprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vprintf(const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 0) _INTL_ARG_NONNULL((1));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 0)
+ _INTL_ARG_NONNULL((1)) int vprintf(const char* __format,
+ va_list __args) {
+ return libintl_vprintf(__format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef vprintf
+#define vprintf libintl_vprintf
+#endif
+extern int vprintf(const char*, va_list) _INTL_ASM(libintl_vprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(1, 0) _INTL_ARG_NONNULL((1));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vprintf;
+}
+#endif
+#endif
+#endif
+
+#if !((defined sprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_sprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vsprintf(char*, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3)
+ _INTL_ARG_NONNULL((1, 2)) int sprintf(char* __result,
+ const char* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vsprintf(__result, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_SPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef sprintf
+#define sprintf libintl_sprintf
+#endif
+extern int sprintf(char*, const char*, ...) _INTL_ASM(libintl_sprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_sprintf;
+}
+#endif
+#endif
+#endif
+#if !((defined vsprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_vsprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !(defined __MINGW32__ || defined _MSC_VER)
+extern int libintl_vsprintf(char*, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0)
+ _INTL_ARG_NONNULL((1, 2)) int vsprintf(char* __result,
+ const char* __format,
+ va_list __args) {
+ return libintl_vsprintf(__result, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VSPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || defined _MSC_VER
+#undef vsprintf
+#define vsprintf libintl_vsprintf
+#endif
+extern int vsprintf(char*, const char*, va_list) _INTL_ASM(libintl_vsprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__ || \
+ defined _MSC_VER) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vsprintf;
+}
+#endif
+#endif
+#endif
+
+#if 1
+
+#if !((defined snprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_snprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vsnprintf(char*, size_t, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 0) _INTL_ARG_NONNULL((3));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 4)
+ _INTL_ARG_NONNULL((3)) int snprintf(char* __result,
+ size_t __maxlen,
+ const char* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vsnprintf(__result, __maxlen, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_SNPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef snprintf
+#define snprintf libintl_snprintf
+#endif
+extern int snprintf(char*, size_t, const char*, ...) _INTL_ASM(libintl_snprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 4) _INTL_ARG_NONNULL((3));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_snprintf;
+}
+#endif
+#endif
+#endif
+#if !((defined vsnprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_vsnprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vsnprintf(char*, size_t, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 0) _INTL_ARG_NONNULL((3));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 0)
+ _INTL_ARG_NONNULL((3)) int vsnprintf(char* __result,
+ size_t __maxlen,
+ const char* __format,
+ va_list __args) {
+ return libintl_vsnprintf(__result, __maxlen, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VSNPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef vsnprintf
+#define vsnprintf libintl_vsnprintf
+#endif
+extern int vsnprintf(char*, size_t, const char*, va_list)
+ _INTL_ASM(libintl_vsnprintf) _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(3, 0)
+ _INTL_ARG_NONNULL((3));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vsnprintf;
+}
+#endif
+#endif
+#endif
+
+#endif
+
+#if 1
+
+#if !((defined asprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_asprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vasprintf(char**, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3)
+ _INTL_ARG_NONNULL((1, 2)) int asprintf(char** __result,
+ const char* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vasprintf(__result, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_ASPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef asprintf
+#define asprintf libintl_asprintf
+#endif
+extern int asprintf(char**, const char*, ...) _INTL_ASM(libintl_asprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 3) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_asprintf;
+}
+#endif
+#endif
+#endif
+#if !((defined vasprintf && defined _GL_STDIO_H) || \
+ defined GNULIB_overrides_vasprintf) /* don't override gnulib */
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vasprintf(char**, const char*, va_list)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0)
+ _INTL_ARG_NONNULL((1, 2)) int vasprintf(char** __result,
+ const char* __format,
+ va_list __args) {
+ return libintl_vasprintf(__result, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VASPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef vasprintf
+#define vasprintf libintl_vasprintf
+#endif
+extern int vasprintf(char**, const char*, va_list) _INTL_ASM(libintl_vasprintf)
+ _INTL_ATTRIBUTE_FORMAT_PRINTF_STANDARD(2, 0) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vasprintf;
+}
+#endif
+#endif
+#endif
+
+#endif
+
+#if 1
+
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vfwprintf(FILE*, const wchar_t*, va_list)
+ _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1, 2)) int fwprintf(FILE* __stream,
+ const wchar_t* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vfwprintf(__stream, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_FWPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef fwprintf
+#define fwprintf libintl_fwprintf
+#endif
+extern int fwprintf(FILE*, const wchar_t*, ...) _INTL_ASM(libintl_fwprintf)
+ _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_fwprintf;
+}
+#endif
+#endif
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vfwprintf(FILE*, const wchar_t*, va_list)
+ _INTL_ARG_NONNULL((1, 2));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1, 2)) int vfwprintf(FILE* __stream,
+ const wchar_t* __format,
+ va_list __args) {
+ return libintl_vfwprintf(__stream, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VFWPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef vfwprintf
+#define vfwprintf libintl_vfwprintf
+#endif
+extern int vfwprintf(FILE*, const wchar_t*, va_list)
+ _INTL_ASM(libintl_vfwprintf) _INTL_ARG_NONNULL((1, 2));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vfwprintf;
+}
+#endif
+#endif
+
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vwprintf(const wchar_t*, va_list) _INTL_ARG_NONNULL((1));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1)) int wprintf(const wchar_t* __format, ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vwprintf(__format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_WPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef wprintf
+#define wprintf libintl_wprintf
+#endif
+extern int wprintf(const wchar_t*, ...) _INTL_ASM(libintl_wprintf)
+ _INTL_ARG_NONNULL((1));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_wprintf;
+}
+#endif
+#endif
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vwprintf(const wchar_t*, va_list) _INTL_ARG_NONNULL((1));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1)) int vwprintf(const wchar_t* __format,
+ va_list __args) {
+ return libintl_vwprintf(__format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VWPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef vwprintf
+#define vwprintf libintl_vwprintf
+#endif
+extern int vwprintf(const wchar_t*, va_list) _INTL_ASM(libintl_vwprintf)
+ _INTL_ARG_NONNULL((1));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vwprintf;
+}
+#endif
+#endif
+
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vswprintf(wchar_t*, size_t, const wchar_t*, va_list)
+ _INTL_ARG_NONNULL((1, 3));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1, 3)) int swprintf(wchar_t* __result,
+ size_t __maxlen,
+ const wchar_t* __format,
+ ...) {
+ va_list __args;
+ int __ret;
+ va_start(__args, __format);
+ __ret = libintl_vswprintf(__result, __maxlen, __format, __args);
+ va_end(__args);
+ return __ret;
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_SWPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef swprintf
+#define swprintf libintl_swprintf
+#endif
+extern int swprintf(wchar_t*, size_t, const wchar_t*, ...)
+ _INTL_ASM(libintl_swprintf) _INTL_ARG_NONNULL((1, 3));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_swprintf;
+}
+#endif
+#endif
+#if defined _INTL_REDIRECT_INLINE && !defined __MINGW32__
+extern int libintl_vswprintf(wchar_t*, size_t, const wchar_t*, va_list)
+ _INTL_ARG_NONNULL((1, 3));
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE
+ _INTL_ARG_NONNULL((1, 3)) int vswprintf(wchar_t* __result,
+ size_t __maxlen,
+ const wchar_t* __format,
+ va_list __args) {
+ return libintl_vswprintf(__result, __maxlen, __format, __args);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_VSWPRINTF
+#if defined _INTL_REDIRECT_MACROS || defined __MINGW32__
+#undef vswprintf
+#define vswprintf libintl_vswprintf
+#endif
+extern int vswprintf(wchar_t*, size_t, const wchar_t*, va_list)
+ _INTL_ASM(libintl_vswprintf) _INTL_ARG_NONNULL((1, 3));
+#if (defined _INTL_REDIRECT_MACROS || defined __MINGW32__) && \
+ defined __cplusplus && !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_vswprintf;
+}
+#endif
+#endif
+
+#endif
+
+#endif
+
+/* Support for retrieving the name of a locale_t object. */
+#if 0
+
+#ifndef GNULIB_defined_newlocale /* don't override gnulib */
+#ifdef _INTL_REDIRECT_INLINE
+extern locale_t libintl_newlocale(int, const char*, locale_t);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE locale_t
+ newlocale(int __category_mask, const char* __name, locale_t __base) {
+ return libintl_newlocale(__category_mask, __name, __base);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_NEWLOCALE
+#ifdef _INTL_REDIRECT_MACROS
+#undef newlocale
+#define newlocale libintl_newlocale
+#endif
+extern locale_t newlocale(int, const char*, locale_t)
+ _INTL_ASM(libintl_newlocale);
+#if defined _INTL_REDIRECT_MACROS && defined __cplusplus && \
+ !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_newlocale;
+}
+#endif
+#endif
+#endif
+
+#ifndef GNULIB_defined_duplocale /* don't override gnulib */
+#ifdef _INTL_REDIRECT_INLINE
+extern locale_t libintl_duplocale(locale_t);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE locale_t
+ duplocale(locale_t __locale) {
+ return libintl_duplocale(__locale);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_DUPLOCALE
+#ifdef _INTL_REDIRECT_MACROS
+#undef duplocale
+#define duplocale libintl_duplocale
+#endif
+extern locale_t duplocale(locale_t) _INTL_ASM(libintl_duplocale);
+#if defined _INTL_REDIRECT_MACROS && defined __cplusplus && \
+ !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_duplocale;
+}
+#endif
+#endif
+#endif
+
+#ifndef GNULIB_defined_freelocale /* don't override gnulib */
+#ifdef _INTL_REDIRECT_INLINE
+extern void libintl_freelocale(locale_t);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE void
+ freelocale(locale_t __locale) {
+ libintl_freelocale(__locale);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_FREELOCALE
+#ifdef _INTL_REDIRECT_MACROS
+#undef freelocale
+#define freelocale libintl_freelocale
+#endif
+extern void freelocale(locale_t) _INTL_ASM(libintl_freelocale);
+#if defined _INTL_REDIRECT_MACROS && defined __cplusplus && \
+ !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_freelocale;
+}
+#endif
+#endif
+#endif
+
+#endif
+
+/* Support for the locale chosen by the user. */
+#if (defined __APPLE__ && defined __MACH__) || defined _WIN32 || \
+ defined __CYGWIN__
+
+#ifndef GNULIB_defined_setlocale /* don't override gnulib */
+#ifdef _INTL_REDIRECT_INLINE
+extern char* libintl_setlocale(int, const char*);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE char*
+ setlocale(int __category, const char* __locale) {
+ return libintl_setlocale(__category, __locale);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_SETLOCALE
+#ifdef _INTL_REDIRECT_MACROS
+#undef setlocale
+#define setlocale libintl_setlocale
+#endif
+extern char* setlocale(int, const char*) _INTL_ASM(libintl_setlocale);
+#if defined _INTL_REDIRECT_MACROS && defined __cplusplus && \
+ !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_setlocale;
+}
+#endif
+#endif
+#endif
+
+#if 1
+
+/* Declare newlocale() only if the system headers define the 'locale_t' type. */
+#if !(defined __CYGWIN__ && !defined LC_ALL_MASK)
+#ifdef _INTL_REDIRECT_INLINE
+extern locale_t libintl_newlocale(int, const char*, locale_t);
+#ifndef __cplusplus
+static
+#endif
+ inline _INTL_FORCE_INLINE locale_t
+ newlocale(int __category_mask, const char* __name, locale_t __base) {
+ return libintl_newlocale(__category_mask, __name, __base);
+}
+#elif !defined _INTL_NO_DEFINE_MACRO_NEWLOCALE
+#ifdef _INTL_REDIRECT_MACROS
+#undef newlocale
+#define newlocale libintl_newlocale
+#endif
+extern locale_t newlocale(int, const char*, locale_t)
+ _INTL_ASM(libintl_newlocale);
+#if defined _INTL_REDIRECT_MACROS && defined __cplusplus && \
+ !defined _INTL_CXX_NO_CLOBBER_STD_NAMESPACE
+namespace std {
+using ::libintl_newlocale;
+}
+#endif
+#endif
+#endif
+
+#endif
+
+#endif
+
+/* Support for relocatable packages. */
+
+/* Sets the original and the current installation prefix of the package.
+ Relocation simply replaces a pathname starting with the original prefix
+ by the corresponding pathname with the current prefix instead. Both
+ prefixes should be directory names without trailing slash (i.e. use ""
+ instead of "/"). */
+#define libintl_set_relocation_prefix libintl_set_relocation_prefix
+extern void libintl_set_relocation_prefix(const char* orig_prefix,
+ const char* curr_prefix);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* libintl.h */
diff --git a/vendor/libghostty-vt/pkg/libpng/build.zig b/vendor/libghostty-vt/pkg/libpng/build.zig
new file mode 100644
index 00000000..dbedac63
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libpng/build.zig
@@ -0,0 +1,91 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addLibrary(.{
+ .name = "png",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ if (target.result.os.tag == .linux) {
+ lib.linkSystemLibrary("m");
+ }
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ if (b.systemIntegrationOption("zlib", .{})) {
+ lib.linkSystemLibrary2("zlib", dynamic_link_opts);
+ } else {
+ if (b.lazyDependency(
+ "zlib",
+ .{ .target = target, .optimize = optimize },
+ )) |zlib_dep| {
+ lib.linkLibrary(zlib_dep.artifact("z"));
+ lib.addIncludePath(b.path(""));
+ }
+
+ if (b.lazyDependency("libpng", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ }
+ }
+
+ if (b.lazyDependency("libpng", .{})) |upstream| {
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DPNG_ARM_NEON_OPT=0",
+ "-DPNG_POWERPC_VSX_OPT=0",
+ "-DPNG_INTEL_SSE_OPT=0",
+ "-DPNG_MIPS_MSA_OPT=0",
+ });
+
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+
+ lib.installHeader(b.path("pnglibconf.h"), "pnglibconf.h");
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+}
+
+const srcs: []const []const u8 = &.{
+ "png.c",
+ "pngerror.c",
+ "pngget.c",
+ "pngmem.c",
+ "pngpread.c",
+ "pngread.c",
+ "pngrio.c",
+ "pngrtran.c",
+ "pngrutil.c",
+ "pngset.c",
+ "pngtrans.c",
+ "pngwio.c",
+ "pngwrite.c",
+ "pngwtran.c",
+ "pngwutil.c",
+};
diff --git a/vendor/libghostty-vt/pkg/libpng/build.zig.zon b/vendor/libghostty-vt/pkg/libpng/build.zig.zon
new file mode 100644
index 00000000..b5153ff7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libpng/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .libpng,
+ .version = "1.6.43",
+ .fingerprint = 0xb7a09eb437ca8a79,
+ .paths = .{""},
+ .dependencies = .{
+ // glennrp/libpng
+ .libpng = .{
+ .url = "https://deps.files.ghostty.org/libpng-1220aa013f0c83da3fb64ea6d327f9173fa008d10e28bc9349eac3463457723b1c66.tar.gz",
+ .hash = "N-V-__8AAJrvXQCqAT8Mg9o_tk6m0yf5Fz-gCNEOKLyTSerD",
+ .lazy = true,
+ },
+
+ .zlib = .{ .path = "../zlib", .lazy = true },
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/libpng/pnglibconf.h b/vendor/libghostty-vt/pkg/libpng/pnglibconf.h
new file mode 100644
index 00000000..b39c38c7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libpng/pnglibconf.h
@@ -0,0 +1,219 @@
+/* pnglibconf.h - library build configuration */
+
+/* libpng version 1.6.38.git */
+
+/* Copyright (c) 2018-2020 Cosmin Truta */
+/* Copyright (c) 1998-2002,2004,2006-2018 Glenn Randers-Pehrson */
+
+/* This code is released under the libpng license. */
+/* For conditions of distribution and use, see the disclaimer */
+/* and license in png.h */
+
+/* pnglibconf.h */
+/* Machine generated file: DO NOT EDIT */
+/* Derived from: scripts/pnglibconf.dfa */
+#ifndef PNGLCONF_H
+#define PNGLCONF_H
+/* options */
+#define PNG_16BIT_SUPPORTED
+#define PNG_ALIGNED_MEMORY_SUPPORTED
+/*#undef PNG_ARM_NEON_API_SUPPORTED*/
+/*#undef PNG_ARM_NEON_CHECK_SUPPORTED*/
+#define PNG_BENIGN_ERRORS_SUPPORTED
+#define PNG_BENIGN_READ_ERRORS_SUPPORTED
+/*#undef PNG_BENIGN_WRITE_ERRORS_SUPPORTED*/
+#define PNG_BUILD_GRAYSCALE_PALETTE_SUPPORTED
+#define PNG_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_COLORSPACE_SUPPORTED
+#define PNG_CONSOLE_IO_SUPPORTED
+#define PNG_CONVERT_tIME_SUPPORTED
+#define PNG_EASY_ACCESS_SUPPORTED
+/*#undef PNG_ERROR_NUMBERS_SUPPORTED*/
+#define PNG_ERROR_TEXT_SUPPORTED
+#define PNG_FIXED_POINT_SUPPORTED
+#define PNG_FLOATING_ARITHMETIC_SUPPORTED
+#define PNG_FLOATING_POINT_SUPPORTED
+#define PNG_FORMAT_AFIRST_SUPPORTED
+#define PNG_FORMAT_BGR_SUPPORTED
+#define PNG_GAMMA_SUPPORTED
+#define PNG_GET_PALETTE_MAX_SUPPORTED
+#define PNG_HANDLE_AS_UNKNOWN_SUPPORTED
+#define PNG_INCH_CONVERSIONS_SUPPORTED
+#define PNG_INFO_IMAGE_SUPPORTED
+#define PNG_IO_STATE_SUPPORTED
+#define PNG_MNG_FEATURES_SUPPORTED
+#define PNG_POINTER_INDEXING_SUPPORTED
+/*#undef PNG_POWERPC_VSX_API_SUPPORTED*/
+/*#undef PNG_POWERPC_VSX_CHECK_SUPPORTED*/
+#define PNG_PROGRESSIVE_READ_SUPPORTED
+#define PNG_READ_16BIT_SUPPORTED
+#define PNG_READ_ALPHA_MODE_SUPPORTED
+#define PNG_READ_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_READ_BACKGROUND_SUPPORTED
+#define PNG_READ_BGR_SUPPORTED
+#define PNG_READ_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_READ_COMPOSITE_NODIV_SUPPORTED
+#define PNG_READ_COMPRESSED_TEXT_SUPPORTED
+#define PNG_READ_EXPAND_16_SUPPORTED
+#define PNG_READ_EXPAND_SUPPORTED
+#define PNG_READ_FILLER_SUPPORTED
+#define PNG_READ_GAMMA_SUPPORTED
+#define PNG_READ_GET_PALETTE_MAX_SUPPORTED
+#define PNG_READ_GRAY_TO_RGB_SUPPORTED
+#define PNG_READ_INTERLACING_SUPPORTED
+#define PNG_READ_INT_FUNCTIONS_SUPPORTED
+#define PNG_READ_INVERT_ALPHA_SUPPORTED
+#define PNG_READ_INVERT_SUPPORTED
+#define PNG_READ_OPT_PLTE_SUPPORTED
+#define PNG_READ_PACKSWAP_SUPPORTED
+#define PNG_READ_PACK_SUPPORTED
+#define PNG_READ_QUANTIZE_SUPPORTED
+#define PNG_READ_RGB_TO_GRAY_SUPPORTED
+#define PNG_READ_SCALE_16_TO_8_SUPPORTED
+#define PNG_READ_SHIFT_SUPPORTED
+#define PNG_READ_STRIP_16_TO_8_SUPPORTED
+#define PNG_READ_STRIP_ALPHA_SUPPORTED
+#define PNG_READ_SUPPORTED
+#define PNG_READ_SWAP_ALPHA_SUPPORTED
+#define PNG_READ_SWAP_SUPPORTED
+#define PNG_READ_TEXT_SUPPORTED
+#define PNG_READ_TRANSFORMS_SUPPORTED
+#define PNG_READ_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_READ_USER_CHUNKS_SUPPORTED
+#define PNG_READ_USER_TRANSFORM_SUPPORTED
+#define PNG_READ_bKGD_SUPPORTED
+#define PNG_READ_cHRM_SUPPORTED
+#define PNG_READ_eXIf_SUPPORTED
+#define PNG_READ_gAMA_SUPPORTED
+#define PNG_READ_hIST_SUPPORTED
+#define PNG_READ_iCCP_SUPPORTED
+#define PNG_READ_iTXt_SUPPORTED
+#define PNG_READ_oFFs_SUPPORTED
+#define PNG_READ_pCAL_SUPPORTED
+#define PNG_READ_pHYs_SUPPORTED
+#define PNG_READ_sBIT_SUPPORTED
+#define PNG_READ_sCAL_SUPPORTED
+#define PNG_READ_sPLT_SUPPORTED
+#define PNG_READ_sRGB_SUPPORTED
+#define PNG_READ_tEXt_SUPPORTED
+#define PNG_READ_tIME_SUPPORTED
+#define PNG_READ_tRNS_SUPPORTED
+#define PNG_READ_zTXt_SUPPORTED
+#define PNG_SAVE_INT_32_SUPPORTED
+#define PNG_SAVE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SEQUENTIAL_READ_SUPPORTED
+#define PNG_SETJMP_SUPPORTED
+#define PNG_SET_OPTION_SUPPORTED
+#define PNG_SET_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_SET_USER_LIMITS_SUPPORTED
+#define PNG_SIMPLIFIED_READ_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_READ_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_READ_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_AFIRST_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_BGR_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_STDIO_SUPPORTED
+#define PNG_SIMPLIFIED_WRITE_SUPPORTED
+#define PNG_STDIO_SUPPORTED
+#define PNG_STORE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_TEXT_SUPPORTED
+#define PNG_TIME_RFC1123_SUPPORTED
+#define PNG_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_USER_CHUNKS_SUPPORTED
+#define PNG_USER_LIMITS_SUPPORTED
+#define PNG_USER_MEM_SUPPORTED
+#define PNG_USER_TRANSFORM_INFO_SUPPORTED
+#define PNG_USER_TRANSFORM_PTR_SUPPORTED
+#define PNG_WARNINGS_SUPPORTED
+#define PNG_WRITE_16BIT_SUPPORTED
+#define PNG_WRITE_ANCILLARY_CHUNKS_SUPPORTED
+#define PNG_WRITE_BGR_SUPPORTED
+#define PNG_WRITE_CHECK_FOR_INVALID_INDEX_SUPPORTED
+#define PNG_WRITE_COMPRESSED_TEXT_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_COMPRESSION_SUPPORTED
+#define PNG_WRITE_CUSTOMIZE_ZTXT_COMPRESSION_SUPPORTED
+#define PNG_WRITE_FILLER_SUPPORTED
+#define PNG_WRITE_FILTER_SUPPORTED
+#define PNG_WRITE_FLUSH_SUPPORTED
+#define PNG_WRITE_GET_PALETTE_MAX_SUPPORTED
+#define PNG_WRITE_INTERLACING_SUPPORTED
+#define PNG_WRITE_INT_FUNCTIONS_SUPPORTED
+#define PNG_WRITE_INVERT_ALPHA_SUPPORTED
+#define PNG_WRITE_INVERT_SUPPORTED
+#define PNG_WRITE_OPTIMIZE_CMF_SUPPORTED
+#define PNG_WRITE_PACKSWAP_SUPPORTED
+#define PNG_WRITE_PACK_SUPPORTED
+#define PNG_WRITE_SHIFT_SUPPORTED
+#define PNG_WRITE_SUPPORTED
+#define PNG_WRITE_SWAP_ALPHA_SUPPORTED
+#define PNG_WRITE_SWAP_SUPPORTED
+#define PNG_WRITE_TEXT_SUPPORTED
+#define PNG_WRITE_TRANSFORMS_SUPPORTED
+#define PNG_WRITE_UNKNOWN_CHUNKS_SUPPORTED
+#define PNG_WRITE_USER_TRANSFORM_SUPPORTED
+#define PNG_WRITE_WEIGHTED_FILTER_SUPPORTED
+#define PNG_WRITE_bKGD_SUPPORTED
+#define PNG_WRITE_cHRM_SUPPORTED
+#define PNG_WRITE_eXIf_SUPPORTED
+#define PNG_WRITE_gAMA_SUPPORTED
+#define PNG_WRITE_hIST_SUPPORTED
+#define PNG_WRITE_iCCP_SUPPORTED
+#define PNG_WRITE_iTXt_SUPPORTED
+#define PNG_WRITE_oFFs_SUPPORTED
+#define PNG_WRITE_pCAL_SUPPORTED
+#define PNG_WRITE_pHYs_SUPPORTED
+#define PNG_WRITE_sBIT_SUPPORTED
+#define PNG_WRITE_sCAL_SUPPORTED
+#define PNG_WRITE_sPLT_SUPPORTED
+#define PNG_WRITE_sRGB_SUPPORTED
+#define PNG_WRITE_tEXt_SUPPORTED
+#define PNG_WRITE_tIME_SUPPORTED
+#define PNG_WRITE_tRNS_SUPPORTED
+#define PNG_WRITE_zTXt_SUPPORTED
+#define PNG_bKGD_SUPPORTED
+#define PNG_cHRM_SUPPORTED
+#define PNG_eXIf_SUPPORTED
+#define PNG_gAMA_SUPPORTED
+#define PNG_hIST_SUPPORTED
+#define PNG_iCCP_SUPPORTED
+#define PNG_iTXt_SUPPORTED
+#define PNG_oFFs_SUPPORTED
+#define PNG_pCAL_SUPPORTED
+#define PNG_pHYs_SUPPORTED
+#define PNG_sBIT_SUPPORTED
+#define PNG_sCAL_SUPPORTED
+#define PNG_sPLT_SUPPORTED
+#define PNG_sRGB_SUPPORTED
+#define PNG_tEXt_SUPPORTED
+#define PNG_tIME_SUPPORTED
+#define PNG_tRNS_SUPPORTED
+#define PNG_zTXt_SUPPORTED
+/* end of options */
+/* settings */
+#define PNG_API_RULE 0
+#define PNG_DEFAULT_READ_MACROS 1
+#define PNG_GAMMA_THRESHOLD_FIXED 5000
+#define PNG_IDAT_READ_SIZE PNG_ZBUF_SIZE
+#define PNG_INFLATE_BUF_SIZE 1024
+#define PNG_LINKAGE_API extern
+#define PNG_LINKAGE_CALLBACK extern
+#define PNG_LINKAGE_DATA extern
+#define PNG_LINKAGE_FUNCTION extern
+#define PNG_MAX_GAMMA_8 11
+#define PNG_QUANTIZE_BLUE_BITS 5
+#define PNG_QUANTIZE_GREEN_BITS 5
+#define PNG_QUANTIZE_RED_BITS 5
+#define PNG_TEXT_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_TEXT_Z_DEFAULT_STRATEGY 0
+#define PNG_USER_CHUNK_CACHE_MAX 1000
+#define PNG_USER_CHUNK_MALLOC_MAX 8000000
+#define PNG_USER_HEIGHT_MAX 1000000
+#define PNG_USER_WIDTH_MAX 1000000
+#define PNG_ZBUF_SIZE 8192
+#define PNG_ZLIB_VERNUM 0 /* unknown */
+#define PNG_Z_DEFAULT_COMPRESSION (-1)
+#define PNG_Z_DEFAULT_NOFILTER_STRATEGY 0
+#define PNG_Z_DEFAULT_STRATEGY 1
+#define PNG_sCAL_PRECISION 5
+#define PNG_sRGB_PROFILE_CHECKS 2
+/* end of settings */
+#endif /* PNGLCONF_H */
diff --git a/vendor/libghostty-vt/pkg/libxml2/build.zig b/vendor/libghostty-vt/pkg/libxml2/build.zig
new file mode 100644
index 00000000..a9b3e4b1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libxml2/build.zig
@@ -0,0 +1,227 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const upstream_ = b.lazyDependency("libxml2", .{});
+
+ const lib = b.addLibrary(.{
+ .name = "xml2",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+
+ if (upstream_) |upstream| lib.addIncludePath(upstream.path("include"));
+ lib.addIncludePath(b.path("override/include"));
+ if (target.result.os.tag == .windows) {
+ lib.addIncludePath(b.path("override/config/win32"));
+ lib.linkSystemLibrary("ws2_32");
+ } else {
+ lib.addIncludePath(b.path("override/config/posix"));
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ // Version info, hardcoded
+ comptime "-DLIBXML_VERSION=" ++ Version.number(),
+ comptime "-DLIBXML_VERSION_STRING=" ++ Version.string(),
+ "-DLIBXML_VERSION_EXTRA=\"\"",
+ comptime "-DLIBXML_DOTTED_VERSION=" ++ Version.dottedString(),
+
+ // These might now always be true (particularly Windows) but for
+ // now we just set them all. We should do some detection later.
+ "-DSEND_ARG2_CAST=",
+ "-DGETHOSTBYNAME_ARG_CAST=",
+ "-DGETHOSTBYNAME_ARG_CAST_CONST=",
+
+ // Always on
+ "-DLIBXML_STATIC=1",
+ "-DLIBXML_AUTOMATA_ENABLED=1",
+ "-DWITHOUT_TRIO=1",
+ });
+ if (target.result.os.tag != .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_ARPA_INET_H=1",
+ "-DHAVE_ARPA_NAMESER_H=1",
+ "-DHAVE_DL_H=1",
+ "-DHAVE_NETDB_H=1",
+ "-DHAVE_NETINET_IN_H=1",
+ "-DHAVE_PTHREAD_H=1",
+ "-DHAVE_SHLLOAD=1",
+ "-DHAVE_SYS_DIR_H=1",
+ "-DHAVE_SYS_MMAN_H=1",
+ "-DHAVE_SYS_NDIR_H=1",
+ "-DHAVE_SYS_SELECT_H=1",
+ "-DHAVE_SYS_SOCKET_H=1",
+ "-DHAVE_SYS_TIMEB_H=1",
+ "-DHAVE_SYS_TIME_H=1",
+ "-DHAVE_SYS_TYPES_H=1",
+ });
+ }
+
+ // Enable our `./configure` options. For bool-type fields we translate
+ // it to the `LIBXML_{field}_ENABLED` C define where field is uppercased.
+ inline for (std.meta.fields(Options)) |field| {
+ const opt = b.option(bool, field.name, "Configure flag") orelse
+ @as(*const bool, @ptrCast(field.default_value_ptr.?)).*;
+ if (opt) {
+ var nameBuf: [32]u8 = undefined;
+ const name = std.ascii.upperString(&nameBuf, field.name);
+ const define = try std.fmt.allocPrint(b.allocator, "-DLIBXML_{s}_ENABLED=1", .{name});
+ try flags.append(b.allocator, define);
+
+ if (std.mem.eql(u8, field.name, "history")) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_LIBHISTORY=1",
+ "-DHAVE_LIBREADLINE=1",
+ });
+ }
+ if (std.mem.eql(u8, field.name, "mem_debug")) {
+ try flags.append(b.allocator, "-DDEBUG_MEMORY_LOCATION=1");
+ }
+ if (std.mem.eql(u8, field.name, "regexp")) {
+ try flags.append(b.allocator, "-DLIBXML_UNICODE_ENABLED=1");
+ }
+ if (std.mem.eql(u8, field.name, "run_debug")) {
+ try flags.append(b.allocator, "-DLIBXML_DEBUG_RUNTIME=1");
+ }
+ if (std.mem.eql(u8, field.name, "thread")) {
+ try flags.append(b.allocator, "-DHAVE_LIBPTHREAD=1");
+ }
+ }
+ }
+
+ if (upstream_) |upstream| {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+
+ lib.installHeader(
+ b.path("override/include/libxml/xmlversion.h"),
+ "libxml/xmlversion.h",
+ );
+ lib.installHeadersDirectory(
+ upstream.path("include"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+}
+
+/// The version information for this library. This is hardcoded for now but
+/// in the future we will parse this from configure.ac.
+pub const Version = struct {
+ pub const major = "2";
+ pub const minor = "11";
+ pub const micro = "5";
+
+ pub fn number() []const u8 {
+ return comptime major ++ "0" ++ minor ++ "0" ++ micro;
+ }
+
+ pub fn string() []const u8 {
+ return comptime "\"" ++ number() ++ "\"";
+ }
+
+ pub fn dottedString() []const u8 {
+ return comptime "\"" ++ major ++ "." ++ minor ++ "." ++ micro ++ "\"";
+ }
+};
+
+/// Compile-time options for the library. These mostly correspond to
+/// options exposed by the native build system used by the library.
+/// These are mapped to `b.option` calls.
+const Options = struct {
+ // These options are all defined in libxml2's configure.c and correspond
+ // to `--with-X` options for `./configure`. Their defaults are properly set.
+ c14n: bool = true,
+ catalog: bool = true,
+ debug: bool = true,
+ ftp: bool = false,
+ history: bool = true,
+ html: bool = true,
+ iconv: bool = true,
+ icu: bool = false,
+ iso8859x: bool = true,
+ legacy: bool = false,
+ mem_debug: bool = false,
+ minimum: bool = true,
+ output: bool = true,
+ pattern: bool = true,
+ push: bool = true,
+ reader: bool = true,
+ regexp: bool = true,
+ run_debug: bool = false,
+ sax1: bool = true,
+ schemas: bool = true,
+ schematron: bool = true,
+ thread: bool = true,
+ thread_alloc: bool = false,
+ tree: bool = true,
+ valid: bool = true,
+ writer: bool = true,
+ xinclude: bool = true,
+ xpath: bool = true,
+ xptr: bool = true,
+ xptr_locs: bool = false,
+ modules: bool = true,
+ lzma: bool = false,
+ zlib: bool = false,
+};
+
+const srcs = &.{
+ "buf.c",
+ "c14n.c",
+ "catalog.c",
+ "chvalid.c",
+ "debugXML.c",
+ "dict.c",
+ "encoding.c",
+ "entities.c",
+ "error.c",
+ "globals.c",
+ "hash.c",
+ "HTMLparser.c",
+ "HTMLtree.c",
+ "legacy.c",
+ "list.c",
+ "nanoftp.c",
+ "nanohttp.c",
+ "parser.c",
+ "parserInternals.c",
+ "pattern.c",
+ "relaxng.c",
+ "SAX.c",
+ "SAX2.c",
+ "schematron.c",
+ "threads.c",
+ "tree.c",
+ "uri.c",
+ "valid.c",
+ "xinclude.c",
+ "xlink.c",
+ "xmlIO.c",
+ "xmlmemory.c",
+ "xmlmodule.c",
+ "xmlreader.c",
+ "xmlregexp.c",
+ "xmlsave.c",
+ "xmlschemas.c",
+ "xmlschemastypes.c",
+ "xmlstring.c",
+ "xmlunicode.c",
+ "xmlwriter.c",
+ "xpath.c",
+ "xpointer.c",
+ "xzlib.c",
+};
diff --git a/vendor/libghostty-vt/pkg/libxml2/build.zig.zon b/vendor/libghostty-vt/pkg/libxml2/build.zig.zon
new file mode 100644
index 00000000..f932835a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libxml2/build.zig.zon
@@ -0,0 +1,13 @@
+.{
+ .name = .libxml2,
+ .version = "2.11.5",
+ .fingerprint = 0xf268267b0b8b040d,
+ .paths = .{""},
+ .dependencies = .{
+ .libxml2 = .{
+ .url = "https://deps.files.ghostty.org/libxml2-2.11.5.tar.gz",
+ .hash = "N-V-__8AAG3RoQEyRC2Vw7Qoro5SYBf62IHn3HjqtNVY6aWK",
+ .lazy = true,
+ },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/libxml2/override/config/posix/config.h b/vendor/libghostty-vt/pkg/libxml2/override/config/posix/config.h
new file mode 100644
index 00000000..44228498
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libxml2/override/config/posix/config.h
@@ -0,0 +1,80 @@
+// This recreates parts of the generated config.h from cmake. Most of the
+// defines actually happen directly in libxml2.zig. Some of these SHOULD
+// be converted to build-time determined.
+
+/* Whether struct sockaddr::__ss_family exists */
+// #define HAVE_BROKEN_SS_FAMILY 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Have dlopen based dso */
+#define HAVE_DLOPEN 1
+
+/* Define to 1 if you have the <fcntl.h> header file. */
+#define HAVE_FCNTL_H 1
+
+/* Define to 1 if you have the `ftime' function. */
+#define HAVE_FTIME 1
+
+/* Define if getaddrinfo is there */
+#define HAVE_GETADDRINFO 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#define HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the `isascii' function. */
+#define HAVE_ISASCII 1
+
+/* Define to 1 if you have the `mmap' function. */
+#define HAVE_MMAP 1
+
+/* Define to 1 if you have the `munmap' function. */
+#define HAVE_MUNMAP 1
+
+/* mmap() is no good without munmap() */
+#if defined(HAVE_MMAP) && !defined(HAVE_MUNMAP)
+# undef /**/ HAVE_MMAP
+#endif
+
+/* Define to 1 if you have the <poll.h> header file. */
+#define HAVE_POLL_H 1
+
+/* Define to 1 if you have the `putenv' function. */
+#define HAVE_PUTENV 1
+
+/* Define to 1 if you have the `rand_r' function. */
+#define HAVE_RAND_R 1
+
+/* Define to 1 if you have the <resolv.h> header file. */
+#define HAVE_RESOLV_H 1
+
+/* Define to 1 if you have the `stat' function. */
+#define HAVE_STAT 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Whether va_copy() is available */
+#define HAVE_VA_COPY 1
+
+/* Define to 1 if you have the <zlib.h> header file. */
+#define HAVE_ZLIB_H 1
+
+/* Whether __va_copy() is available */
+#define HAVE___VA_COPY 1
+
+/* Support for IPv6 */
+#define SUPPORT_IP6 1
+
+/* Define if va_list is an array type */
+#define VA_LIST_IS_ARRAY 1
diff --git a/vendor/libghostty-vt/pkg/libxml2/override/config/win32/config.h b/vendor/libghostty-vt/pkg/libxml2/override/config/win32/config.h
new file mode 100644
index 00000000..5108d08c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libxml2/override/config/win32/config.h
@@ -0,0 +1,20 @@
+#ifndef __LIBXML_WIN32_CONFIG__
+#define __LIBXML_WIN32_CONFIG__
+
+#define SEND_ARG2_CAST
+#define GETHOSTBYNAME_ARG_CAST
+
+#define HAVE_SYS_STAT_H
+#define HAVE_STAT
+#define HAVE_FCNTL_H
+
+#if defined(__MINGW32__) || (defined(_MSC_VER) && _MSC_VER >= 1600)
+#define HAVE_STDINT_H
+#endif
+
+#if defined(_MSC_VER) && _MSC_VER < 1900
+#define snprintf _snprintf
+#define vsnprintf _vsnprintf
+#endif
+
+#endif /* __LIBXML_WIN32_CONFIG__ */
diff --git a/vendor/libghostty-vt/pkg/libxml2/override/include/libxml/xmlversion.h b/vendor/libghostty-vt/pkg/libxml2/override/include/libxml/xmlversion.h
new file mode 100644
index 00000000..039d704d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/libxml2/override/include/libxml/xmlversion.h
@@ -0,0 +1,18 @@
+// This recreates parts of the generated libxml/xmlversion.h.in that we need
+// to build libxml2 without actually templating the header file. We define most
+// of the defines in that file using flags to the compiler in libxml2.zig
+
+#ifndef __XML_VERSION_H__
+#define __XML_VERSION_H__
+
+#include <libxml/xmlexports.h>
+
+// We are not GCC.
+#define XML_IGNORE_FPTR_CAST_WARNINGS
+#define XML_POP_WARNINGS
+#define LIBXML_ATTR_FORMAT(fmt,args)
+#define LIBXML_ATTR_ALLOC_SIZE(x)
+#define ATTRIBUTE_UNUSED
+#define XML_DEPRECATED
+
+#endif
diff --git a/vendor/libghostty-vt/pkg/macos/animation.zig b/vendor/libghostty-vt/pkg/macos/animation.zig
new file mode 100644
index 00000000..247f9760
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/animation.zig
@@ -0,0 +1,10 @@
+pub const c = @import("animation/c.zig").c;
+
+/// https://developer.apple.com/documentation/quartzcore/calayer/contents_gravity_values?language=objc
+pub extern "c" const kCAGravityTopLeft: *anyopaque;
+pub extern "c" const kCAGravityBottomLeft: *anyopaque;
+pub extern "c" const kCAGravityCenter: *anyopaque;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/animation/c.zig b/vendor/libghostty-vt/pkg/macos/animation/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/animation/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/build.zig b/vendor/libghostty-vt/pkg/macos/build.zig
new file mode 100644
index 00000000..0525e481
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/build.zig
@@ -0,0 +1,82 @@
+const std = @import("std");
+const builtin = @import("builtin");
+const apple_sdk = @import("apple_sdk");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const module = b.addModule("macos", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const lib = b.addLibrary(.{
+ .name = "macos",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+
+ lib.addCSourceFile(.{
+ .file = b.path("os/zig_macos.c"),
+ .flags = &.{"-std=c99"},
+ });
+ lib.addCSourceFile(.{
+ .file = b.path("text/ext.c"),
+ });
+ lib.linkFramework("CoreFoundation");
+ lib.linkFramework("CoreGraphics");
+ lib.linkFramework("CoreText");
+ lib.linkFramework("CoreVideo");
+ lib.linkFramework("QuartzCore");
+ lib.linkFramework("IOSurface");
+ if (target.result.os.tag == .macos) {
+ lib.linkFramework("Carbon");
+ module.linkFramework("Carbon", .{});
+ }
+
+ if (target.result.os.tag.isDarwin()) {
+ module.linkFramework("CoreFoundation", .{});
+ module.linkFramework("CoreGraphics", .{});
+ module.linkFramework("CoreText", .{});
+ module.linkFramework("CoreVideo", .{});
+ module.linkFramework("QuartzCore", .{});
+ module.linkFramework("IOSurface", .{});
+
+ try apple_sdk.addPaths(b, lib);
+ }
+ b.installArtifact(lib);
+
+ {
+ const test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ if (target.result.os.tag.isDarwin()) {
+ try apple_sdk.addPaths(b, test_exe);
+ }
+ test_exe.linkLibrary(lib);
+
+ var it = module.import_table.iterator();
+ while (it.next()) |entry| {
+ test_exe.root_module.addImport(
+ entry.key_ptr.*,
+ entry.value_ptr.*,
+ );
+ }
+
+ b.installArtifact(test_exe);
+
+ const tests_run = b.addRunArtifact(test_exe);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/build.zig.zon b/vendor/libghostty-vt/pkg/macos/build.zig.zon
new file mode 100644
index 00000000..4c22ad7c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/build.zig.zon
@@ -0,0 +1,9 @@
+.{
+ .name = .macos,
+ .version = "0.1.0",
+ .fingerprint = 0x45e2f6107d5b2b2c,
+ .paths = .{""},
+ .dependencies = .{
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/macos/carbon.zig b/vendor/libghostty-vt/pkg/macos/carbon.zig
new file mode 100644
index 00000000..8eafaffe
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/carbon.zig
@@ -0,0 +1,5 @@
+pub const c = @import("carbon/c.zig").c;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/carbon/c.zig b/vendor/libghostty-vt/pkg/macos/carbon/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/carbon/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/dispatch.zig b/vendor/libghostty-vt/pkg/macos/dispatch.zig
new file mode 100644
index 00000000..3add9c0e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/dispatch.zig
@@ -0,0 +1,18 @@
+pub const c = @import("dispatch/c.zig").c;
+pub const data = @import("dispatch/data.zig");
+pub const queue = @import("dispatch/queue.zig");
+pub const Data = data.Data;
+
+pub extern "c" fn dispatch_sync(
+ queue: *anyopaque,
+ block: *anyopaque,
+) void;
+
+pub extern "c" fn dispatch_async(
+ queue: *anyopaque,
+ block: *anyopaque,
+) void;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/dispatch/c.zig b/vendor/libghostty-vt/pkg/macos/dispatch/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/dispatch/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/dispatch/data.zig b/vendor/libghostty-vt/pkg/macos/dispatch/data.zig
new file mode 100644
index 00000000..28df24b7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/dispatch/data.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const Data = opaque {
+ pub const DESTRUCTOR_DEFAULT = c.DISPATCH_DATA_DESTRUCTOR_DEFAULT;
+
+ pub fn create(
+ data: []const u8,
+ queue: ?*anyopaque,
+ destructor: ?*anyopaque,
+ ) !*Data {
+ return dispatch_data_create(
+ data.ptr,
+ data.len,
+ queue,
+ destructor,
+ ) orelse return error.OutOfMemory;
+ }
+
+ pub fn release(data: *Data) void {
+ foundation.c.CFRelease(data);
+ }
+};
+
+extern "c" fn dispatch_data_create(
+ data: [*]const u8,
+ len: usize,
+ queue: ?*anyopaque,
+ destructor: ?*anyopaque,
+) ?*Data;
diff --git a/vendor/libghostty-vt/pkg/macos/dispatch/queue.zig b/vendor/libghostty-vt/pkg/macos/dispatch/queue.zig
new file mode 100644
index 00000000..cb2ca01c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/dispatch/queue.zig
@@ -0,0 +1,8 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+pub const Queue = *anyopaque; // dispatch_queue_t
+
+pub fn getMain() Queue {
+ return c.dispatch_get_main_queue().?;
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation.zig b/vendor/libghostty-vt/pkg/macos/foundation.zig
new file mode 100644
index 00000000..d4f63409
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation.zig
@@ -0,0 +1,37 @@
+const array = @import("foundation/array.zig");
+const attributed_string = @import("foundation/attributed_string.zig");
+const base = @import("foundation/base.zig");
+const character_set = @import("foundation/character_set.zig");
+const data = @import("foundation/data.zig");
+const dictionary = @import("foundation/dictionary.zig");
+const number = @import("foundation/number.zig");
+const string = @import("foundation/string.zig");
+const typepkg = @import("foundation/type.zig");
+const url = @import("foundation/url.zig");
+
+pub const c = @import("foundation/c.zig").c;
+pub const Array = array.Array;
+pub const MutableArray = array.MutableArray;
+pub const AttributedString = attributed_string.AttributedString;
+pub const MutableAttributedString = attributed_string.MutableAttributedString;
+pub const ComparisonResult = base.ComparisonResult;
+pub const Range = base.Range;
+pub const FourCharCode = base.FourCharCode;
+pub const CharacterSet = character_set.CharacterSet;
+pub const Data = data.Data;
+pub const Dictionary = dictionary.Dictionary;
+pub const MutableDictionary = dictionary.MutableDictionary;
+pub const Number = number.Number;
+pub const String = string.String;
+pub const MutableString = string.MutableString;
+pub const StringComparison = string.StringComparison;
+pub const StringEncoding = string.StringEncoding;
+pub const stringGetSurrogatePairForLongCharacter = string.stringGetSurrogatePairForLongCharacter;
+pub const URL = url.URL;
+pub const URLPathStyle = url.URLPathStyle;
+pub const CFRelease = typepkg.CFRelease;
+pub const CFRetain = typepkg.CFRetain;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/array.zig b/vendor/libghostty-vt/pkg/macos/foundation/array.zig
new file mode 100644
index 00000000..7b580eb0
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/array.zig
@@ -0,0 +1,176 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const base = @import("base.zig");
+const c = @import("c.zig").c;
+const cftype = @import("type.zig");
+const ComparisonResult = base.ComparisonResult;
+const Range = base.Range;
+
+pub const Array = opaque {
+ pub fn create(comptime T: type, values: []*const T) Allocator.Error!*Array {
+ return CFArrayCreate(
+ null,
+ @ptrCast(values.ptr),
+ @intCast(values.len),
+ null,
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn release(self: *Array) void {
+ cftype.CFRelease(self);
+ }
+
+ pub fn getCount(self: *Array) usize {
+ return CFArrayGetCount(self);
+ }
+
+ /// Note the return type is actually a `*const T` but we strip the
+ /// constness so that further API calls work correctly. The Foundation
+ /// API doesn't properly mark things const/non-const.
+ pub fn getValueAtIndex(self: *Array, comptime T: type, idx: usize) *T {
+ return @ptrCast(@alignCast(CFArrayGetValueAtIndex(self, idx)));
+ }
+
+ pub extern "c" fn CFArrayCreate(
+ allocator: ?*anyopaque,
+ values: [*]*const anyopaque,
+ num_values: usize,
+ callbacks: ?*const anyopaque,
+ ) ?*Array;
+ pub extern "c" fn CFArrayGetCount(*Array) usize;
+ pub extern "c" fn CFArrayGetValueAtIndex(*Array, usize) *anyopaque;
+ extern "c" var kCFTypeArrayCallBacks: anyopaque;
+};
+
+pub const MutableArray = opaque {
+ pub fn create() Allocator.Error!*MutableArray {
+ return CFArrayCreateMutable(
+ null,
+ 0,
+ &c.kCFTypeArrayCallBacks,
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn createCopy(array: *Array) Allocator.Error!*MutableArray {
+ return CFArrayCreateMutableCopy(
+ null,
+ 0,
+ array,
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn release(self: *MutableArray) void {
+ cftype.CFRelease(self);
+ }
+
+ pub fn appendValue(
+ self: *MutableArray,
+ comptime Elem: type,
+ value: *const Elem,
+ ) void {
+ CFArrayAppendValue(self, @ptrCast(@constCast(value)));
+ }
+
+ pub fn removeValue(self: *MutableArray, idx: usize) void {
+ CFArrayRemoveValueAtIndex(self, idx);
+ }
+
+ pub fn sortValues(
+ self: *MutableArray,
+ comptime Elem: type,
+ comptime Context: type,
+ context: ?*Context,
+ comptime comparator: ?*const fn (
+ a: *const Elem,
+ b: *const Elem,
+ context: ?*Context,
+ ) callconv(.c) ComparisonResult,
+ ) void {
+ CFArraySortValues(
+ self,
+ Range.init(0, Array.CFArrayGetCount(@ptrCast(self))),
+ comparator,
+ context,
+ );
+ }
+
+ extern "c" fn CFArrayCreateMutable(
+ allocator: ?*anyopaque,
+ capacity: usize,
+ callbacks: ?*const anyopaque,
+ ) ?*MutableArray;
+ extern "c" fn CFArrayCreateMutableCopy(
+ allocator: ?*anyopaque,
+ capacity: usize,
+ array: *Array,
+ ) ?*MutableArray;
+ extern "c" fn CFArrayAppendValue(
+ *MutableArray,
+ *anyopaque,
+ ) void;
+ extern "c" fn CFArrayRemoveValueAtIndex(
+ *MutableArray,
+ usize,
+ ) void;
+ extern "c" fn CFArraySortValues(
+ array: *MutableArray,
+ range: Range,
+ comparator: ?*const anyopaque,
+ context: ?*anyopaque,
+ ) void;
+};
+
+test "array" {
+ const testing = std.testing;
+
+ const str = "hello";
+ var values = [_]*const u8{ &str[0], &str[1] };
+ const arr = try Array.create(u8, &values);
+ defer arr.release();
+
+ try testing.expectEqual(@as(usize, 2), arr.getCount());
+
+ {
+ const ch = arr.getValueAtIndex(u8, 0);
+ try testing.expectEqual(@as(u8, 'h'), ch.*);
+ }
+
+ // Can make it mutable
+ var mut = try MutableArray.createCopy(arr);
+ defer mut.release();
+}
+
+test "array sorting" {
+ const testing = std.testing;
+
+ const str = "hello";
+ var values = [_]*const u8{ &str[0], &str[1] };
+ const arr = try Array.create(u8, &values);
+ defer arr.release();
+ const mut = try MutableArray.createCopy(arr);
+ defer mut.release();
+
+ mut.sortValues(
+ u8,
+ void,
+ null,
+ struct {
+ fn compare(a: *const u8, b: *const u8, _: ?*void) callconv(.c) ComparisonResult {
+ if (a.* > b.*) return .greater;
+ if (a.* == b.*) return .equal;
+ return .less;
+ }
+ }.compare,
+ );
+
+ {
+ const mutarr: *Array = @ptrCast(mut);
+ const ch = mutarr.getValueAtIndex(u8, 0);
+ try testing.expectEqual(@as(u8, 'e'), ch.*);
+ }
+ {
+ const mutarr: *Array = @ptrCast(mut);
+ const ch = mutarr.getValueAtIndex(u8, 1);
+ try testing.expectEqual(@as(u8, 'h'), ch.*);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig b/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig
new file mode 100644
index 00000000..c7f27d7d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/attributed_string.zig
@@ -0,0 +1,103 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const AttributedString = opaque {
+ pub fn create(
+ str: *foundation.String,
+ attributes: *foundation.Dictionary,
+ ) Allocator.Error!*AttributedString {
+ return @ptrCast(@constCast(c.CFAttributedStringCreate(
+ null,
+ @ptrCast(str),
+ @ptrCast(attributes),
+ ) orelse return Allocator.Error.OutOfMemory));
+ }
+
+ pub fn release(self: *AttributedString) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getLength(self: *AttributedString) usize {
+ return @intCast(c.CFAttributedStringGetLength(@ptrCast(self)));
+ }
+
+ pub fn getString(self: *AttributedString) *foundation.String {
+ return @ptrFromInt(@intFromPtr(
+ c.CFAttributedStringGetString(@ptrCast(self)),
+ ));
+ }
+};
+
+pub const MutableAttributedString = opaque {
+ pub fn create(cap: usize) Allocator.Error!*MutableAttributedString {
+ return @as(
+ ?*MutableAttributedString,
+ @ptrFromInt(@intFromPtr(c.CFAttributedStringCreateMutable(
+ null,
+ @intCast(cap),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *MutableAttributedString) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn replaceString(
+ self: *MutableAttributedString,
+ range: foundation.Range,
+ replacement: *foundation.String,
+ ) void {
+ c.CFAttributedStringReplaceString(
+ @ptrCast(self),
+ @bitCast(range),
+ @ptrCast(replacement),
+ );
+ }
+
+ pub fn setAttribute(
+ self: *MutableAttributedString,
+ range: foundation.Range,
+ key: anytype,
+ value: ?*anyopaque,
+ ) void {
+ const T = @TypeOf(key);
+ const info = @typeInfo(T);
+ const Key = if (info != .pointer) T else info.pointer.child;
+ const key_arg = if (@hasDecl(Key, "key"))
+ key.key()
+ else
+ key;
+
+ c.CFAttributedStringSetAttribute(
+ @ptrCast(self),
+ @bitCast(range),
+ @ptrCast(key_arg),
+ value,
+ );
+ }
+
+ pub fn getLength(self: *MutableAttributedString) usize {
+ return @intCast(c.CFAttributedStringGetLength(@ptrCast(self)));
+ }
+};
+
+test "mutable attributed string" {
+ //const testing = std.testing;
+
+ const str = try MutableAttributedString.create(0);
+ defer str.release();
+
+ {
+ const rep = try foundation.String.createWithBytes("hello", .utf8, false);
+ defer rep.release();
+ str.replaceString(foundation.Range.init(0, 0), rep);
+ }
+
+ str.setAttribute(foundation.Range.init(0, 0), text.FontAttribute.url, null);
+ str.setAttribute(foundation.Range.init(0, 0), text.FontAttribute.name.key(), null);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/base.zig b/vendor/libghostty-vt/pkg/macos/foundation/base.zig
new file mode 100644
index 00000000..b926523f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/base.zig
@@ -0,0 +1,35 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const ComparisonResult = enum(c_int) {
+ less = -1,
+ equal = 0,
+ greater = 1,
+};
+
+pub const Range = extern struct {
+ location: c.CFIndex,
+ length: c.CFIndex,
+
+ pub fn init(loc: usize, len: usize) Range {
+ return @bitCast(c.CFRangeMake(@intCast(loc), @intCast(len)));
+ }
+};
+
+pub const FourCharCode = packed struct(u32) {
+ d: u8,
+ c: u8,
+ b: u8,
+ a: u8,
+
+ pub fn init(v: *const [4]u8) FourCharCode {
+ return .{ .a = v[0], .b = v[1], .c = v[2], .d = v[3] };
+ }
+
+ /// Converts the ID to a string. The return value is only valid
+ /// for the lifetime of the self pointer.
+ pub fn str(self: FourCharCode) [4]u8 {
+ return .{ self.a, self.b, self.c, self.d };
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/c.zig b/vendor/libghostty-vt/pkg/macos/foundation/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig b/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig
new file mode 100644
index 00000000..d80ca45d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/character_set.zig
@@ -0,0 +1,49 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const CharacterSet = opaque {
+ pub fn createWithCharactersInString(
+ str: *foundation.String,
+ ) Allocator.Error!*CharacterSet {
+ return @as(?*CharacterSet, @ptrFromInt(@intFromPtr(c.CFCharacterSetCreateWithCharactersInString(
+ null,
+ @ptrCast(str),
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createWithCharactersInRange(
+ range: foundation.Range,
+ ) Allocator.Error!*CharacterSet {
+ return @as(?*CharacterSet, @ptrFromInt(@intFromPtr(c.CFCharacterSetCreateWithCharactersInRange(
+ null,
+ @bitCast(range),
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *CharacterSet) void {
+ c.CFRelease(self);
+ }
+};
+
+test "character set" {
+ //const testing = std.testing;
+
+ const str = try foundation.String.createWithBytes("hello world", .ascii, false);
+ defer str.release();
+
+ const cs = try CharacterSet.createWithCharactersInString(str);
+ defer cs.release();
+}
+
+test "character set range" {
+ //const testing = std.testing;
+
+ const cs = try CharacterSet.createWithCharactersInRange(.{
+ .location = 'A',
+ .length = 1,
+ });
+ defer cs.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/data.zig b/vendor/libghostty-vt/pkg/macos/foundation/data.zig
new file mode 100644
index 00000000..c06da65b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/data.zig
@@ -0,0 +1,38 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const Data = opaque {
+ pub fn createWithBytesNoCopy(data: []const u8) Allocator.Error!*Data {
+ return @as(
+ ?*Data,
+ @ptrFromInt(@intFromPtr(c.CFDataCreateWithBytesNoCopy(
+ null,
+ data.ptr,
+ @intCast(data.len),
+ c.kCFAllocatorNull,
+ ))),
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn release(self: *Data) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getPointer(self: *Data) [*]const u8 {
+ return @ptrCast(c.CFDataGetBytePtr(@ptrCast(self)));
+ }
+
+ pub fn getLength(self: *Data) usize {
+ return @intCast(c.CFDataGetLength(@ptrCast(self)));
+ }
+};
+
+test {
+ //const testing = std.testing;
+
+ const raw = "hello world";
+ const data = try Data.createWithBytesNoCopy(raw);
+ defer data.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig b/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig
new file mode 100644
index 00000000..a529442a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/dictionary.zig
@@ -0,0 +1,125 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const Dictionary = opaque {
+ pub fn create(
+ keys: ?[]const ?*const anyopaque,
+ values: ?[]const ?*const anyopaque,
+ ) Allocator.Error!*Dictionary {
+ if (keys != null or values != null) {
+ assert(keys != null);
+ assert(values != null);
+ assert(keys.?.len == values.?.len);
+ }
+
+ return @as(?*Dictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreate(
+ null,
+ @ptrCast(@constCast(if (keys) |slice| slice.ptr else null)),
+ @ptrCast(@constCast(if (values) |slice| slice.ptr else null)),
+ @intCast(if (keys) |slice| slice.len else 0),
+ &c.kCFTypeDictionaryKeyCallBacks,
+ &c.kCFTypeDictionaryValueCallBacks,
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Dictionary) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getCount(self: *Dictionary) usize {
+ return @intCast(c.CFDictionaryGetCount(@ptrCast(self)));
+ }
+
+ pub fn getValue(self: *Dictionary, comptime V: type, key: ?*const anyopaque) ?*V {
+ return @ptrFromInt(@intFromPtr(c.CFDictionaryGetValue(
+ @ptrCast(self),
+ key,
+ )));
+ }
+
+ pub fn getKeysAndValues(self: *Dictionary, alloc: Allocator) !struct {
+ keys: []?*const anyopaque,
+ values: []?*const anyopaque,
+ } {
+ const count = self.getCount();
+ const keys = try alloc.alloc(?*const anyopaque, count);
+ errdefer alloc.free(keys);
+ const values = try alloc.alloc(?*const anyopaque, count);
+ errdefer alloc.free(values);
+ c.CFDictionaryGetKeysAndValues(
+ @ptrCast(self),
+ @ptrCast(keys.ptr),
+ @ptrCast(values.ptr),
+ );
+ return .{ .keys = keys, .values = values };
+ }
+};
+
+pub const MutableDictionary = opaque {
+ pub fn create(cap: usize) Allocator.Error!*MutableDictionary {
+ return @as(?*MutableDictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreateMutable(
+ null,
+ @intCast(cap),
+ &c.kCFTypeDictionaryKeyCallBacks,
+ &c.kCFTypeDictionaryValueCallBacks,
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createMutableCopy(cap: usize, src: *Dictionary) Allocator.Error!*MutableDictionary {
+ return @as(?*MutableDictionary, @ptrFromInt(@intFromPtr(c.CFDictionaryCreateMutableCopy(
+ null,
+ @intCast(cap),
+ @ptrCast(src),
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *MutableDictionary) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn setValue(self: *MutableDictionary, key: ?*const anyopaque, value: ?*const anyopaque) void {
+ c.CFDictionarySetValue(
+ @ptrCast(self),
+ key,
+ value,
+ );
+ }
+};
+
+test "dictionary" {
+ const testing = std.testing;
+
+ const str = try foundation.String.createWithBytes("hello", .unicode, false);
+ defer str.release();
+
+ var keys = [_]?*const anyopaque{c.kCFURLIsPurgeableKey};
+ var values = [_]?*const anyopaque{str};
+ const dict = try Dictionary.create(&keys, &values);
+ defer dict.release();
+
+ try testing.expectEqual(@as(usize, 1), dict.getCount());
+ try testing.expect(dict.getValue(foundation.String, c.kCFURLIsPurgeableKey) != null);
+ try testing.expect(dict.getValue(foundation.String, c.kCFURLIsVolumeKey) == null);
+}
+
+test "mutable dictionary" {
+ const testing = std.testing;
+
+ const dict = try MutableDictionary.create(0);
+ defer dict.release();
+
+ const str = try foundation.String.createWithBytes("hello", .unicode, false);
+ defer str.release();
+
+ dict.setValue(c.kCFURLIsPurgeableKey, str);
+
+ {
+ const imm = @as(*Dictionary, @ptrCast(dict));
+ try testing.expectEqual(@as(usize, 1), imm.getCount());
+ try testing.expect(imm.getValue(foundation.String, c.kCFURLIsPurgeableKey) != null);
+ try testing.expect(imm.getValue(foundation.String, c.kCFURLIsVolumeKey) == null);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/number.zig b/vendor/libghostty-vt/pkg/macos/foundation/number.zig
new file mode 100644
index 00000000..88bf9edf
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/number.zig
@@ -0,0 +1,79 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const Number = opaque {
+ pub fn create(
+ comptime type_: NumberType,
+ value: *const type_.ValueType(),
+ ) Allocator.Error!*Number {
+ return @as(?*Number, @ptrFromInt(@intFromPtr(c.CFNumberCreate(
+ null,
+ @intFromEnum(type_),
+ value,
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn getValue(self: *const Number, comptime t: NumberType, ptr: *t.ValueType()) bool {
+ return c.CFNumberGetValue(
+ @ptrCast(self),
+ @intFromEnum(t),
+ ptr,
+ ) == 1;
+ }
+
+ pub fn release(self: *Number) void {
+ c.CFRelease(self);
+ }
+};
+
+pub const NumberType = enum(c.CFNumberType) {
+ sint8 = c.kCFNumberSInt8Type,
+ sint16 = c.kCFNumberSInt16Type,
+ sint32 = c.kCFNumberSInt32Type,
+ sint64 = c.kCFNumberSInt64Type,
+ float32 = c.kCFNumberFloat32Type,
+ float64 = c.kCFNumberFloat64Type,
+ char = c.kCFNumberCharType,
+ short = c.kCFNumberShortType,
+ int = c.kCFNumberIntType,
+ long = c.kCFNumberLongType,
+ long_long = c.kCFNumberLongLongType,
+ float = c.kCFNumberFloatType,
+ double = c.kCFNumberDoubleType,
+ cf_index = c.kCFNumberCFIndexType,
+ ns_integer = c.kCFNumberNSIntegerType,
+ cg_float = c.kCFNumberCGFloatType,
+
+ pub fn ValueType(comptime self: NumberType) type {
+ return switch (self) {
+ .sint8 => i8,
+ .sint16 => i16,
+ .sint32 => i32,
+ .sint64 => i64,
+ .float32 => f32,
+ .float64 => f64,
+ .char => u8,
+ .short => c_short,
+ .int => c_int,
+ .long => c_long,
+ .long_long => c_longlong,
+ .float => f32,
+ .double => f64,
+ else => unreachable, // TODO
+ };
+ }
+};
+
+test {
+ const testing = std.testing;
+
+ const inner: i8 = 42;
+ const v = try Number.create(.sint8, &inner);
+ defer v.release();
+
+ var result: i8 = undefined;
+ try testing.expect(v.getValue(.sint8, &result));
+ try testing.expectEqual(result, inner);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/string.zig b/vendor/libghostty-vt/pkg/macos/foundation/string.zig
new file mode 100644
index 00000000..2f679fef
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/string.zig
@@ -0,0 +1,174 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const String = opaque {
+ pub fn createWithBytes(
+ bs: []const u8,
+ encoding: StringEncoding,
+ external: bool,
+ ) Allocator.Error!*String {
+ return @as(?*String, @ptrFromInt(@intFromPtr(c.CFStringCreateWithBytes(
+ null,
+ bs.ptr,
+ @intCast(bs.len),
+ @intFromEnum(encoding),
+ @intFromBool(external),
+ )))) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createWithCharactersNoCopy(
+ unichars: []const u16,
+ ) *String {
+ return @as(*String, @ptrFromInt(@intFromPtr(c.CFStringCreateWithCharactersNoCopy(
+ null,
+ @ptrCast(unichars.ptr),
+ @intCast(unichars.len),
+ foundation.c.kCFAllocatorNull,
+ ))));
+ }
+
+ pub fn release(self: *String) void {
+ c.CFRelease(self);
+ }
+
+ pub fn getLength(self: *String) usize {
+ return @intCast(c.CFStringGetLength(@ptrCast(self)));
+ }
+
+ pub fn hasPrefix(self: *String, prefix: *String) bool {
+ return c.CFStringHasPrefix(
+ @ptrCast(self),
+ @ptrCast(prefix),
+ ) == 1;
+ }
+
+ pub fn compare(
+ self: *String,
+ other: *String,
+ options: StringComparison,
+ ) foundation.ComparisonResult {
+ return @enumFromInt(c.CFStringCompare(
+ @ptrCast(self),
+ @ptrCast(other),
+ @intCast(@as(c_int, @bitCast(options))),
+ ));
+ }
+
+ pub fn cstring(self: *String, buf: []u8, encoding: StringEncoding) ?[]const u8 {
+ if (c.CFStringGetCString(
+ @ptrCast(self),
+ buf.ptr,
+ @intCast(buf.len),
+ @intFromEnum(encoding),
+ ) == 0) return null;
+ return std.mem.sliceTo(buf, 0);
+ }
+
+ pub fn cstringPtr(self: *String, encoding: StringEncoding) ?[:0]const u8 {
+ const ptr = c.CFStringGetCStringPtr(
+ @ptrCast(self),
+ @intFromEnum(encoding),
+ );
+ if (ptr == null) return null;
+ return std.mem.sliceTo(ptr, 0);
+ }
+};
+
+pub const MutableString = opaque {
+ pub fn create(cap: usize) !*MutableString {
+ return @ptrCast(c.CFStringCreateMutable(
+ null,
+ @intCast(cap),
+ ) orelse return Allocator.Error.OutOfMemory);
+ }
+
+ pub fn release(self: *MutableString) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn string(self: *MutableString) *String {
+ return @ptrCast(self);
+ }
+
+ pub fn appendCharacters(self: *MutableString, chars: []const u16) void {
+ c.CFStringAppendCharacters(
+ @ptrCast(self),
+ chars.ptr,
+ @intCast(chars.len),
+ );
+ }
+};
+
+pub const StringComparison = packed struct {
+ case_insensitive: bool = false,
+ _unused_2: bool = false,
+ backwards: bool = false,
+ anchored: bool = false,
+ nonliteral: bool = false,
+ localized: bool = false,
+ numerically: bool = false,
+ diacritic_insensitive: bool = false,
+ width_insensitive: bool = false,
+ forced_ordering: bool = false,
+ _padding: u22 = 0,
+
+ test {
+ try std.testing.expectEqual(@bitSizeOf(c_int), @bitSizeOf(StringComparison));
+ }
+};
+
+/// https://developer.apple.com/documentation/corefoundation/cfstringencoding?language=objc
+pub const StringEncoding = enum(u32) {
+ invalid = 0xffffffff,
+ mac_roman = 0,
+ windows_latin1 = 0x0500,
+ iso_latin1 = 0x0201,
+ nextstep_latin = 0x0B01,
+ ascii = 0x0600,
+ unicode = 0x0100,
+ utf8 = 0x08000100,
+ non_lossy_ascii = 0x0BFF,
+ utf16_be = 0x10000100,
+ utf16_le = 0x14000100,
+ utf32 = 0x0c000100,
+ utf32_be = 0x18000100,
+ utf32_le = 0x1c000100,
+};
+
+pub fn stringGetSurrogatePairForLongCharacter(
+ ch: u32,
+ surrogates: []u16,
+) bool {
+ assert(surrogates.len >= 2);
+ return c.CFStringGetSurrogatePairForLongCharacter(ch, surrogates.ptr) == 1;
+}
+
+test "string" {
+ const testing = std.testing;
+
+ const str = try String.createWithBytes("hello world", .ascii, false);
+ defer str.release();
+
+ const prefix = try String.createWithBytes("hello", .ascii, false);
+ defer prefix.release();
+
+ try testing.expect(str.hasPrefix(prefix));
+ try testing.expectEqual(foundation.ComparisonResult.equal, str.compare(str, .{}));
+ try testing.expectEqualStrings("hello world", str.cstringPtr(.ascii).?);
+
+ {
+ var buf: [128]u8 = undefined;
+ const cstr = str.cstring(&buf, .ascii).?;
+ try testing.expectEqualStrings("hello world", cstr);
+ }
+}
+
+test "unichar" {
+ const testing = std.testing;
+
+ var unichars: [2]u16 = undefined;
+ try testing.expect(!stringGetSurrogatePairForLongCharacter('A', &unichars));
+}
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/type.zig b/vendor/libghostty-vt/pkg/macos/foundation/type.zig
new file mode 100644
index 00000000..45bd0905
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/type.zig
@@ -0,0 +1,2 @@
+pub extern "c" fn CFRelease(*anyopaque) void;
+pub extern "c" fn CFRetain(*anyopaque) void;
diff --git a/vendor/libghostty-vt/pkg/macos/foundation/url.zig b/vendor/libghostty-vt/pkg/macos/foundation/url.zig
new file mode 100644
index 00000000..b48a7181
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/foundation/url.zig
@@ -0,0 +1,104 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const URL = opaque {
+ pub fn createWithString(str: *foundation.String, base: ?*URL) Allocator.Error!*URL {
+ return CFURLCreateWithString(
+ null,
+ str,
+ base,
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn createWithFileSystemPath(
+ path: *foundation.String,
+ style: URLPathStyle,
+ dir: bool,
+ ) Allocator.Error!*URL {
+ return @as(
+ ?*URL,
+ @ptrFromInt(@intFromPtr(c.CFURLCreateWithFileSystemPath(
+ null,
+ @ptrCast(path),
+ @intFromEnum(style),
+ if (dir) 1 else 0,
+ ))),
+ ) orelse error.OutOfMemory;
+ }
+
+ pub fn createStringByReplacingPercentEscapes(
+ str: *foundation.String,
+ escape: *foundation.String,
+ ) Allocator.Error!*foundation.String {
+ return CFURLCreateStringByReplacingPercentEscapes(
+ null,
+ str,
+ escape,
+ ) orelse return error.OutOfMemory;
+ }
+
+ pub fn release(self: *URL) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn copyPath(self: *URL) ?*foundation.String {
+ return CFURLCopyPath(self);
+ }
+
+ pub extern "c" fn CFURLCreateWithString(
+ allocator: ?*anyopaque,
+ url_string: *const anyopaque,
+ base_url: ?*const anyopaque,
+ ) ?*URL;
+ pub extern "c" fn CFURLCopyPath(*URL) ?*foundation.String;
+ pub extern "c" fn CFURLCreateStringByReplacingPercentEscapes(
+ allocator: ?*anyopaque,
+ original: *const anyopaque,
+ escape: *const anyopaque,
+ ) ?*foundation.String;
+};
+
+pub const URLPathStyle = enum(c_int) {
+ posix = c.kCFURLPOSIXPathStyle,
+ windows = c.kCFURLWindowsPathStyle,
+};
+
+test {
+ const testing = std.testing;
+
+ const str = try foundation.String.createWithBytes("http://www.example.com/foo", .utf8, false);
+ defer str.release();
+
+ const url = try URL.createWithString(str, null);
+ defer url.release();
+
+ {
+ const path = url.copyPath().?;
+ defer path.release();
+
+ var buf: [128]u8 = undefined;
+ const cstr = path.cstring(&buf, .utf8).?;
+ try testing.expectEqualStrings("/foo", cstr);
+ }
+}
+
+test "path" {
+ const testing = std.testing;
+
+ const str = try foundation.String.createWithBytes("foo/bar.ttf", .utf8, false);
+ defer str.release();
+
+ const url = try URL.createWithFileSystemPath(str, .posix, false);
+ defer url.release();
+
+ {
+ const path = url.copyPath().?;
+ defer path.release();
+
+ var buf: [128]u8 = undefined;
+ const cstr = path.cstring(&buf, .utf8).?;
+ try testing.expectEqualStrings("foo/bar.ttf", cstr);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/graphics.zig b/vendor/libghostty-vt/pkg/macos/graphics.zig
new file mode 100644
index 00000000..5195da4a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics.zig
@@ -0,0 +1,24 @@
+const affine_transform = @import("graphics/affine_transform.zig");
+const bitmap_context = @import("graphics/bitmap_context.zig");
+const color_space = @import("graphics/color_space.zig");
+const font = @import("graphics/font.zig");
+const geometry = @import("graphics/geometry.zig");
+const image = @import("graphics/image.zig");
+const path = @import("graphics/path.zig");
+
+pub const c = @import("graphics/c.zig").c;
+pub const AffineTransform = affine_transform.AffineTransform;
+pub const BitmapContext = bitmap_context.BitmapContext;
+pub const ColorSpace = color_space.ColorSpace;
+pub const Glyph = font.Glyph;
+pub const Point = geometry.Point;
+pub const Rect = geometry.Rect;
+pub const Size = geometry.Size;
+pub const ImageAlphaInfo = image.ImageAlphaInfo;
+pub const BitmapInfo = image.BitmapInfo;
+pub const Path = path.Path;
+pub const MutablePath = path.MutablePath;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/affine_transform.zig b/vendor/libghostty-vt/pkg/macos/graphics/affine_transform.zig
new file mode 100644
index 00000000..e649e7a1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/affine_transform.zig
@@ -0,0 +1,16 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const AffineTransform = extern struct {
+ a: c.CGFloat,
+ b: c.CGFloat,
+ c: c.CGFloat,
+ d: c.CGFloat,
+ tx: c.CGFloat,
+ ty: c.CGFloat,
+
+ pub fn identity() AffineTransform {
+ return @bitCast(c.CGAffineTransformIdentity);
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/bitmap_context.zig b/vendor/libghostty-vt/pkg/macos/graphics/bitmap_context.zig
new file mode 100644
index 00000000..7db5577f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/bitmap_context.zig
@@ -0,0 +1,50 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const graphics = @import("../graphics.zig");
+const Context = @import("context.zig").Context;
+const c = @import("c.zig").c;
+
+pub const BitmapContext = opaque {
+ pub const context = Context(BitmapContext);
+
+ pub fn create(
+ data: ?[]u8,
+ width: usize,
+ height: usize,
+ bits_per_component: usize,
+ bytes_per_row: usize,
+ space: *graphics.ColorSpace,
+ opts: c_uint,
+ ) Allocator.Error!*BitmapContext {
+ return @as(
+ ?*BitmapContext,
+ @ptrFromInt(@intFromPtr(c.CGBitmapContextCreate(
+ @ptrCast(if (data) |d| d.ptr else null),
+ width,
+ height,
+ bits_per_component,
+ bytes_per_row,
+ @ptrCast(space),
+ opts,
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+};
+
+test {
+ //const testing = std.testing;
+
+ const cs = try graphics.ColorSpace.createDeviceGray();
+ defer cs.release();
+ const ctx = try BitmapContext.create(null, 80, 80, 8, 80, cs, 0);
+ const context = BitmapContext.context;
+ defer context.release(ctx);
+ context.setShouldAntialias(ctx, true);
+ context.setShouldSmoothFonts(ctx, false);
+ context.setGrayFillColor(ctx, 1, 1);
+ context.setGrayStrokeColor(ctx, 1, 1);
+ context.setTextDrawingMode(ctx, .fill);
+ context.setTextMatrix(ctx, graphics.AffineTransform.identity());
+ context.setTextPosition(ctx, 0, 0);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/c.zig b/vendor/libghostty-vt/pkg/macos/graphics/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/color_space.zig b/vendor/libghostty-vt/pkg/macos/graphics/color_space.zig
new file mode 100644
index 00000000..16960591
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/color_space.zig
@@ -0,0 +1,99 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+
+pub const ColorSpace = opaque {
+ pub fn createDeviceGray() Allocator.Error!*ColorSpace {
+ return @as(
+ ?*ColorSpace,
+ @ptrFromInt(@intFromPtr(c.CGColorSpaceCreateDeviceGray())),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createDeviceRGB() Allocator.Error!*ColorSpace {
+ return @as(
+ ?*ColorSpace,
+ @ptrFromInt(@intFromPtr(c.CGColorSpaceCreateDeviceRGB())),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createNamed(name: Name) Allocator.Error!*ColorSpace {
+ return @as(
+ ?*ColorSpace,
+ @ptrFromInt(@intFromPtr(c.CGColorSpaceCreateWithName(name.cfstring()))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *ColorSpace) void {
+ c.CGColorSpaceRelease(@ptrCast(self));
+ }
+
+ pub const Name = enum {
+ /// This color space uses the DCI P3 primaries, a D65 white point, and
+ /// the sRGB transfer function.
+ displayP3,
+ /// The Display P3 color space with a linear transfer function and
+ /// extended-range values.
+ extendedLinearDisplayP3,
+ /// The sRGB colorimetry and non-linear transfer function are specified
+ /// in IEC 61966-2-1.
+ sRGB,
+ /// This color space has the same colorimetry as `sRGB`, but uses a
+ /// linear transfer function.
+ linearSRGB,
+ /// This color space has the same colorimetry as `sRGB`, but you can
+ /// encode component values below `0.0` and above `1.0`. Negative values
+ /// are encoded as the signed reflection of the original encoding
+ /// function, as shown in the formula below:
+ /// ```
+ /// extendedTransferFunction(x) = sign(x) * sRGBTransferFunction(abs(x))
+ /// ```
+ extendedSRGB,
+ /// This color space has the same colorimetry as `sRGB`; in addition,
+ /// you may encode component values below `0.0` and above `1.0`.
+ extendedLinearSRGB,
+ /// ...
+ genericGrayGamma2_2,
+ /// ...
+ linearGray,
+ /// This color space has the same colorimetry as `genericGrayGamma2_2`,
+ /// but you can encode component values below `0.0` and above `1.0`.
+ /// Negative values are encoded as the signed reflection of the
+ /// original encoding function, as shown in the formula below:
+ /// ```
+ /// extendedGrayTransferFunction(x) = sign(x) * gamma22Function(abs(x))
+ /// ```
+ extendedGray,
+ /// This color space has the same colorimetry as `linearGray`; in
+ /// addition, you may encode component values below `0.0` and above `1.0`.
+ extendedLinearGray,
+
+ fn cfstring(self: Name) c.CFStringRef {
+ return switch (self) {
+ .displayP3 => c.kCGColorSpaceDisplayP3,
+ .extendedLinearDisplayP3 => c.kCGColorSpaceExtendedLinearDisplayP3,
+ .sRGB => c.kCGColorSpaceSRGB,
+ .extendedSRGB => c.kCGColorSpaceExtendedSRGB,
+ .linearSRGB => c.kCGColorSpaceLinearSRGB,
+ .extendedLinearSRGB => c.kCGColorSpaceExtendedLinearSRGB,
+ .genericGrayGamma2_2 => c.kCGColorSpaceGenericGrayGamma2_2,
+ .extendedGray => c.kCGColorSpaceExtendedGray,
+ .linearGray => c.kCGColorSpaceLinearGray,
+ .extendedLinearGray => c.kCGColorSpaceExtendedLinearGray,
+ };
+ }
+ };
+};
+
+test {
+ //const testing = std.testing;
+
+ const space = try ColorSpace.createDeviceGray();
+ defer space.release();
+}
+
+test {
+ const space = try ColorSpace.createDeviceRGB();
+ defer space.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/context.zig b/vendor/libghostty-vt/pkg/macos/graphics/context.zig
new file mode 100644
index 00000000..77e4344e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/context.zig
@@ -0,0 +1,172 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const graphics = @import("../graphics.zig");
+const c = @import("c.zig").c;
+
+/// Returns a struct that has all the shared context functions for the
+/// given type.
+pub fn Context(comptime T: type) type {
+ return struct {
+ value: *T,
+
+ pub fn release(self: *T) void {
+ c.CGContextRelease(@ptrCast(self));
+ }
+
+ pub fn setLineWidth(self: *T, width: f64) void {
+ c.CGContextSetLineWidth(
+ @ptrCast(self),
+ width,
+ );
+ }
+
+ pub fn setAllowsAntialiasing(self: *T, v: bool) void {
+ c.CGContextSetAllowsAntialiasing(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setAllowsFontSmoothing(self: *T, v: bool) void {
+ c.CGContextSetAllowsFontSmoothing(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setAllowsFontSubpixelPositioning(self: *T, v: bool) void {
+ c.CGContextSetAllowsFontSubpixelPositioning(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setAllowsFontSubpixelQuantization(self: *T, v: bool) void {
+ c.CGContextSetAllowsFontSubpixelQuantization(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setShouldAntialias(self: *T, v: bool) void {
+ c.CGContextSetShouldAntialias(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setShouldSmoothFonts(self: *T, v: bool) void {
+ c.CGContextSetShouldSmoothFonts(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setShouldSubpixelPositionFonts(self: *T, v: bool) void {
+ c.CGContextSetShouldSubpixelPositionFonts(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setShouldSubpixelQuantizeFonts(self: *T, v: bool) void {
+ c.CGContextSetShouldSubpixelQuantizeFonts(
+ @ptrCast(self),
+ v,
+ );
+ }
+
+ pub fn setGrayFillColor(self: *T, gray: f64, alpha: f64) void {
+ c.CGContextSetGrayFillColor(
+ @ptrCast(self),
+ gray,
+ alpha,
+ );
+ }
+
+ pub fn setGrayStrokeColor(self: *T, gray: f64, alpha: f64) void {
+ c.CGContextSetGrayStrokeColor(
+ @ptrCast(self),
+ gray,
+ alpha,
+ );
+ }
+
+ pub fn setRGBFillColor(self: *T, r: f64, g: f64, b: f64, alpha: f64) void {
+ c.CGContextSetRGBFillColor(
+ @ptrCast(self),
+ r,
+ g,
+ b,
+ alpha,
+ );
+ }
+
+ pub fn setRGBStrokeColor(self: *T, r: f64, g: f64, b: f64, alpha: f64) void {
+ c.CGContextSetRGBStrokeColor(
+ @ptrCast(self),
+ r,
+ g,
+ b,
+ alpha,
+ );
+ }
+
+ pub fn setTextDrawingMode(self: *T, mode: TextDrawingMode) void {
+ c.CGContextSetTextDrawingMode(
+ @ptrCast(self),
+ @intFromEnum(mode),
+ );
+ }
+
+ pub fn setTextMatrix(self: *T, matrix: graphics.AffineTransform) void {
+ c.CGContextSetTextMatrix(
+ @ptrCast(self),
+ @bitCast(matrix),
+ );
+ }
+
+ pub fn setTextPosition(self: *T, x: f64, y: f64) void {
+ c.CGContextSetTextPosition(
+ @ptrCast(self),
+ x,
+ y,
+ );
+ }
+
+ pub fn fillRect(self: *T, rect: graphics.Rect) void {
+ c.CGContextFillRect(
+ @ptrCast(self),
+ @bitCast(rect),
+ );
+ }
+
+ pub fn scaleCTM(self: *T, sx: c.CGFloat, sy: c.CGFloat) void {
+ c.CGContextScaleCTM(
+ @ptrCast(self),
+ sx,
+ sy,
+ );
+ }
+
+ pub fn translateCTM(self: *T, tx: c.CGFloat, ty: c.CGFloat) void {
+ c.CGContextTranslateCTM(
+ @ptrCast(self),
+ tx,
+ ty,
+ );
+ }
+ };
+}
+
+pub const TextDrawingMode = enum(c_int) {
+ fill = c.kCGTextFill,
+ stroke = c.kCGTextStroke,
+ fill_stroke = c.kCGTextFillStroke,
+ invisible = c.kCGTextInvisible,
+ fill_clip = c.kCGTextFillClip,
+ stroke_clip = c.kCGTextStrokeClip,
+ fill_stroke_clip = c.kCGTextFillStrokeClip,
+ clip = c.kCGTextClip,
+};
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/font.zig b/vendor/libghostty-vt/pkg/macos/graphics/font.zig
new file mode 100644
index 00000000..c310c62a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/font.zig
@@ -0,0 +1,3 @@
+const c = @import("c.zig").c;
+
+pub const Glyph = c.CGGlyph;
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/geometry.zig b/vendor/libghostty-vt/pkg/macos/graphics/geometry.zig
new file mode 100644
index 00000000..66937403
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/geometry.zig
@@ -0,0 +1,34 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+pub const Point = extern struct {
+ x: c.CGFloat,
+ y: c.CGFloat,
+};
+
+pub const Rect = extern struct {
+ origin: Point,
+ size: Size,
+
+ pub fn init(x: f64, y: f64, width: f64, height: f64) Rect {
+ return @bitCast(c.CGRectMake(x, y, width, height));
+ }
+
+ pub fn isNull(self: Rect) bool {
+ return c.CGRectIsNull(@bitCast(self));
+ }
+
+ pub fn getHeight(self: Rect) c.CGFloat {
+ return c.CGRectGetHeight(@bitCast(self));
+ }
+
+ pub fn getWidth(self: Rect) c.CGFloat {
+ return c.CGRectGetWidth(@bitCast(self));
+ }
+};
+
+pub const Size = extern struct {
+ width: c.CGFloat,
+ height: c.CGFloat,
+};
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/image.zig b/vendor/libghostty-vt/pkg/macos/graphics/image.zig
new file mode 100644
index 00000000..879384b1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/image.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const graphics = @import("../graphics.zig");
+const context = @import("context.zig");
+const c = @import("c.zig").c;
+
+pub const ImageAlphaInfo = enum(c_uint) {
+ none = c.kCGImageAlphaNone,
+ premultiplied_last = c.kCGImageAlphaPremultipliedLast,
+ premultiplied_first = c.kCGImageAlphaPremultipliedFirst,
+ last = c.kCGImageAlphaLast,
+ first = c.kCGImageAlphaFirst,
+ none_skip_last = c.kCGImageAlphaNoneSkipLast,
+ none_skip_first = c.kCGImageAlphaNoneSkipFirst,
+ only = c.kCGImageAlphaOnly,
+};
+
+pub const BitmapInfo = enum(c_uint) {
+ alpha_mask = c.kCGBitmapAlphaInfoMask,
+ float_mask = c.kCGBitmapFloatInfoMask,
+ float_components = c.kCGBitmapFloatComponents,
+ byte_order_mask = c.kCGBitmapByteOrderMask,
+ byte_order_default = c.kCGBitmapByteOrderDefault,
+ byte_order_16_little = c.kCGBitmapByteOrder16Little,
+ byte_order_32_little = c.kCGBitmapByteOrder32Little,
+ byte_order_16_big = c.kCGBitmapByteOrder16Big,
+ byte_order_32_big = c.kCGBitmapByteOrder32Big,
+
+ _,
+};
diff --git a/vendor/libghostty-vt/pkg/macos/graphics/path.zig b/vendor/libghostty-vt/pkg/macos/graphics/path.zig
new file mode 100644
index 00000000..7f549709
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/graphics/path.zig
@@ -0,0 +1,68 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const c = @import("c.zig").c;
+
+pub const Path = opaque {
+ pub fn createWithRect(
+ rect: graphics.Rect,
+ transform: ?*const graphics.AffineTransform,
+ ) Allocator.Error!*Path {
+ return @as(
+ ?*Path,
+ @ptrFromInt(@intFromPtr(c.CGPathCreateWithRect(
+ @bitCast(rect),
+ @ptrCast(transform),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Path) void {
+ foundation.CFRelease(self);
+ }
+};
+
+pub const MutablePath = opaque {
+ pub fn create() Allocator.Error!*MutablePath {
+ return @as(
+ ?*MutablePath,
+ @ptrFromInt(@intFromPtr(c.CGPathCreateMutable())),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *MutablePath) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn addRect(
+ self: *MutablePath,
+ transform: ?*const graphics.AffineTransform,
+ rect: graphics.Rect,
+ ) void {
+ c.CGPathAddRect(
+ @ptrCast(self),
+ @ptrCast(transform),
+ @bitCast(rect),
+ );
+ }
+
+ pub fn getBoundingBox(self: *MutablePath) graphics.Rect {
+ return @bitCast(c.CGPathGetBoundingBox(@ptrCast(self)));
+ }
+};
+
+test "mutable path" {
+ //const testing = std.testing;
+
+ const path = try MutablePath.create();
+ defer path.release();
+
+ path.addRect(null, graphics.Rect.init(0, 0, 100, 200));
+}
+
+test "path from rect" {
+ const path = try Path.createWithRect(graphics.Rect.init(0, 0, 100, 200), null);
+ defer path.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/iosurface.zig b/vendor/libghostty-vt/pkg/macos/iosurface.zig
new file mode 100644
index 00000000..9d2e750c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/iosurface.zig
@@ -0,0 +1,8 @@
+const iosurface = @import("iosurface/iosurface.zig");
+
+pub const c = @import("iosurface/c.zig").c;
+pub const IOSurface = iosurface.IOSurface;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/iosurface/c.zig b/vendor/libghostty-vt/pkg/macos/iosurface/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/iosurface/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/iosurface/iosurface.zig b/vendor/libghostty-vt/pkg/macos/iosurface/iosurface.zig
new file mode 100644
index 00000000..37f8712b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/iosurface/iosurface.zig
@@ -0,0 +1,136 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const video = @import("../video.zig");
+
+pub const IOSurface = opaque {
+ pub const Error = error{
+ InvalidOperation,
+ };
+
+ pub const Properties = struct {
+ width: c_int,
+ height: c_int,
+ pixel_format: video.PixelFormat,
+ bytes_per_element: c_int,
+ colorspace: ?*graphics.ColorSpace,
+ };
+
+ pub fn init(properties: Properties) Allocator.Error!*IOSurface {
+ var w = try foundation.Number.create(.int, &properties.width);
+ defer w.release();
+ var h = try foundation.Number.create(.int, &properties.height);
+ defer h.release();
+ var pf = try foundation.Number.create(.int, &@as(c_int, @intFromEnum(properties.pixel_format)));
+ defer pf.release();
+ var bpe = try foundation.Number.create(.int, &properties.bytes_per_element);
+ defer bpe.release();
+
+ var properties_dict = try foundation.Dictionary.create(
+ &[_]?*const anyopaque{
+ c.kIOSurfaceWidth,
+ c.kIOSurfaceHeight,
+ c.kIOSurfacePixelFormat,
+ c.kIOSurfaceBytesPerElement,
+ },
+ &[_]?*const anyopaque{ w, h, pf, bpe },
+ );
+ defer properties_dict.release();
+
+ var surface = @as(?*IOSurface, @ptrFromInt(@intFromPtr(
+ c.IOSurfaceCreate(@ptrCast(properties_dict)),
+ ))) orelse return error.OutOfMemory;
+
+ if (properties.colorspace) |space| {
+ surface.setColorSpace(space);
+ }
+
+ return surface;
+ }
+
+ pub fn deinit(self: *IOSurface) void {
+ // We mark it purgeable so that it is immediately unloaded, so that we
+ // don't have to wait for CoreFoundation garbage collection to trigger.
+ _ = c.IOSurfaceSetPurgeable(
+ @ptrCast(self),
+ c.kIOSurfacePurgeableEmpty,
+ null,
+ );
+ foundation.CFRelease(self);
+ }
+
+ pub fn retain(self: *IOSurface) void {
+ foundation.CFRetain(self);
+ }
+
+ pub fn release(self: *IOSurface) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn setColorSpace(self: *IOSurface, colorspace: *graphics.ColorSpace) void {
+ const serialized_colorspace = graphics.c.CGColorSpaceCopyPropertyList(
+ @ptrCast(colorspace),
+ ).?;
+ defer foundation.CFRelease(@constCast(serialized_colorspace));
+
+ c.IOSurfaceSetValue(
+ @ptrCast(self),
+ c.kIOSurfaceColorSpace,
+ @ptrCast(serialized_colorspace),
+ );
+ }
+
+ pub inline fn lock(self: *IOSurface) void {
+ c.IOSurfaceLock(
+ @ptrCast(self),
+ 0,
+ null,
+ );
+ }
+ pub inline fn unlock(self: *IOSurface) void {
+ c.IOSurfaceUnlock(
+ @ptrCast(self),
+ 0,
+ null,
+ );
+ }
+
+ pub inline fn getAllocSize(self: *IOSurface) usize {
+ return c.IOSurfaceGetAllocSize(@ptrCast(self));
+ }
+
+ pub inline fn getWidth(self: *IOSurface) usize {
+ return c.IOSurfaceGetWidth(@ptrCast(self));
+ }
+
+ pub inline fn getHeight(self: *IOSurface) usize {
+ return c.IOSurfaceGetHeight(@ptrCast(self));
+ }
+
+ pub inline fn getBytesPerElement(self: *IOSurface) usize {
+ return c.IOSurfaceGetBytesPerElement(@ptrCast(self));
+ }
+
+ pub inline fn getBytesPerRow(self: *IOSurface) usize {
+ return c.IOSurfaceGetBytesPerRow(@ptrCast(self));
+ }
+
+ pub inline fn getBaseAddress(self: *IOSurface) ?[*]u8 {
+ return @ptrCast(c.IOSurfaceGetBaseAddress(@ptrCast(self)));
+ }
+
+ pub inline fn getElementWidth(self: *IOSurface) usize {
+ return c.IOSurfaceGetElementWidth(@ptrCast(self));
+ }
+
+ pub inline fn getElementHeight(self: *IOSurface) usize {
+ return c.IOSurfaceGetElementHeight(@ptrCast(self));
+ }
+
+ pub inline fn getPixelFormat(self: *IOSurface) video.PixelFormat {
+ return @enumFromInt(c.IOSurfaceGetPixelFormat(@ptrCast(self)));
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/main.zig b/vendor/libghostty-vt/pkg/macos/main.zig
new file mode 100644
index 00000000..e4f4b950
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/main.zig
@@ -0,0 +1,35 @@
+const builtin = @import("builtin");
+
+pub const carbon = @import("carbon.zig");
+pub const foundation = @import("foundation.zig");
+pub const animation = @import("animation.zig");
+pub const dispatch = @import("dispatch.zig");
+pub const graphics = @import("graphics.zig");
+pub const os = @import("os.zig");
+pub const text = @import("text.zig");
+pub const video = @import("video.zig");
+pub const iosurface = @import("iosurface.zig");
+
+// All of our C imports consolidated into one place. We used to
+// import them one by one in each package but Zig 0.14 has some
+// kind of issue with that I wasn't able to minimize.
+pub const c = @cImport({
+ @cInclude("CoreFoundation/CoreFoundation.h");
+ @cInclude("CoreGraphics/CoreGraphics.h");
+ @cInclude("CoreText/CoreText.h");
+ @cInclude("CoreVideo/CoreVideo.h");
+ @cInclude("CoreVideo/CVPixelBuffer.h");
+ @cInclude("QuartzCore/CALayer.h");
+ @cInclude("IOSurface/IOSurfaceRef.h");
+ @cInclude("dispatch/dispatch.h");
+ @cInclude("os/log.h");
+ @cInclude("os/signpost.h");
+
+ if (builtin.os.tag == .macos) {
+ @cInclude("Carbon/Carbon.h");
+ }
+});
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/os.zig b/vendor/libghostty-vt/pkg/macos/os.zig
new file mode 100644
index 00000000..9716a9ab
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/os.zig
@@ -0,0 +1,10 @@
+const log = @import("os/log.zig");
+
+pub const c = @import("os/c.zig");
+pub const signpost = @import("os/signpost.zig");
+pub const Log = log.Log;
+pub const LogType = log.LogType;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/os/c.zig b/vendor/libghostty-vt/pkg/macos/os/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/os/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/os/log.zig b/vendor/libghostty-vt/pkg/macos/os/log.zig
new file mode 100644
index 00000000..219c914d
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/os/log.zig
@@ -0,0 +1,65 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+
+pub const Log = opaque {
+ pub fn create(
+ subsystem: [:0]const u8,
+ category: [:0]const u8,
+ ) *Log {
+ return @ptrCast(c.os_log_create(
+ subsystem.ptr,
+ category.ptr,
+ ).?);
+ }
+
+ pub fn release(self: *Log) void {
+ c.os_release(self);
+ }
+
+ pub fn typeEnabled(self: *Log, typ: LogType) bool {
+ return c.os_log_type_enabled(
+ @ptrCast(self),
+ @intFromEnum(typ),
+ );
+ }
+
+ pub fn log(
+ self: *Log,
+ alloc: Allocator,
+ typ: LogType,
+ comptime format: []const u8,
+ args: anytype,
+ ) void {
+ const str = nosuspend std.fmt.allocPrintSentinel(
+ alloc,
+ format,
+ args,
+ 0,
+ ) catch return;
+ defer alloc.free(str);
+ zig_os_log_with_type(self, typ, str.ptr);
+ }
+
+ extern "c" fn zig_os_log_with_type(*Log, LogType, [*c]const u8) void;
+};
+
+/// https://developer.apple.com/documentation/os/os_log_type_t?language=objc
+pub const LogType = enum(c.os_log_type_t) {
+ default = c.OS_LOG_TYPE_DEFAULT,
+ debug = c.OS_LOG_TYPE_DEBUG,
+ info = c.OS_LOG_TYPE_INFO,
+ err = c.OS_LOG_TYPE_ERROR,
+ fault = c.OS_LOG_TYPE_FAULT,
+};
+
+test {
+ const testing = std.testing;
+
+ const log = Log.create("com.mitchellh.ghostty", "test");
+ defer log.release();
+
+ try testing.expect(log.typeEnabled(.fault));
+ log.log(testing.allocator, .default, "hello {d}", .{12});
+}
diff --git a/vendor/libghostty-vt/pkg/macos/os/signpost.zig b/vendor/libghostty-vt/pkg/macos/os/signpost.zig
new file mode 100644
index 00000000..2bd12566
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/os/signpost.zig
@@ -0,0 +1,214 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const logpkg = @import("log.zig");
+const Log = logpkg.Log;
+
+/// This should be called once at the start of the program to intialize
+/// some required state for signpost logging.
+///
+/// This is all to workaround a Zig bug:
+/// https://github.com/ziglang/zig/issues/24370
+pub fn init() void {
+ if (__dso_handle != null) return;
+
+ const sym = comptime sym: {
+ const root = @import("root");
+
+ // If we have a main function, use that as the symbol.
+ if (@hasDecl(root, "main")) break :sym root.main;
+
+ // Otherwise, we're in a library, so we just use the first
+ // function in our root module. I actually don't know if this is
+ // all required or if we can just use the real `__dso_handle` symbol,
+ // but this seems to work for now.
+ for (@typeInfo(root).@"struct".decls) |decl_info| {
+ const decl = @field(root, decl_info.name);
+ if (@typeInfo(@TypeOf(decl)) == .@"fn") break :sym decl;
+ }
+
+ @compileError("no functions found in root module");
+ };
+
+ // Since __dso_handle is not automatically populated by the linker,
+ // we populate it by looking up the main function's module address
+ // which should be a mach-o header.
+ var info: DlInfo = undefined;
+ const result = dladdr(sym, &info);
+ assert(result != 0);
+ __dso_handle = @ptrCast(@alignCast(info.dli_fbase));
+}
+
+/// This should REALLY be an extern var that is populated by the linker,
+/// but there is a Zig bug: https://github.com/ziglang/zig/issues/24370
+var __dso_handle: ?*c.mach_header = null;
+
+// Import the necessary C functions and types
+extern "c" fn dladdr(addr: ?*const anyopaque, info: *DlInfo) c_int;
+
+// Define the Dl_info structure
+const DlInfo = extern struct {
+ dli_fname: [*:0]const u8, // Pathname of shared object
+ dli_fbase: ?*anyopaque, // Base address of shared object
+ dli_sname: [*:0]const u8, // Name of nearest symbol
+ dli_saddr: ?*anyopaque, // Address of nearest symbol
+};
+
+/// Checks whether signpost logging is enabled for the given log handle.
+/// Returns true if signposts will be recorded for this log, false otherwise.
+/// This can be used to avoid expensive operations when signpost logging is disabled.
+///
+/// https://developer.apple.com/documentation/os/os_signpost_enabled?language=objc
+pub fn enabled(log: *Log) bool {
+ return c.os_signpost_enabled(@ptrCast(log));
+}
+
+/// Emits a signpost event - a single point in time marker.
+/// Events are useful for marking when specific actions occur, such as
+/// user interactions, state changes, or other discrete occurrences.
+/// The event will appear as a vertical line in Instruments.
+///
+/// https://developer.apple.com/documentation/os/os_signpost_event_emit?language=objc
+pub fn emitEvent(
+ log: *Log,
+ id: Id,
+ comptime name: [:0]const u8,
+) void {
+ emitWithName(log, id, .event, name);
+}
+
+/// Marks the beginning of a time interval.
+/// Use this with intervalEnd to measure the duration of operations.
+/// The same ID must be used for both the begin and end calls.
+/// Intervals appear as horizontal bars in Instruments timeline.
+///
+/// https://developer.apple.com/documentation/os/os_signpost_interval_begin?language=objc
+pub fn intervalBegin(log: *Log, id: Id, comptime name: [:0]const u8) void {
+ emitWithName(log, id, .interval_begin, name);
+}
+
+/// Marks the end of a time interval.
+/// Must be paired with a prior intervalBegin call using the same ID.
+/// The name should match the name used in intervalBegin.
+/// Instruments will calculate and display the duration between begin and end.
+///
+/// https://developer.apple.com/documentation/os/os_signpost_interval_end?language=objc
+pub fn intervalEnd(log: *Log, id: Id, comptime name: [:0]const u8) void {
+ emitWithName(log, id, .interval_end, name);
+}
+
+/// The internal function to emit a signpost with a specific name.
+fn emitWithName(
+ log: *Log,
+ id: Id,
+ typ: Type,
+ comptime name: [:0]const u8,
+) void {
+ // Init must be called by this point.
+ assert(__dso_handle != null);
+
+ var buf: [2]u8 = @splat(0);
+ c._os_signpost_emit_with_name_impl(
+ __dso_handle,
+ @ptrCast(log),
+ @intFromEnum(typ),
+ @intFromEnum(id),
+ name.ptr,
+ "".ptr,
+ &buf,
+ buf.len,
+ );
+}
+
+/// https://developer.apple.com/documentation/os/os_signpost_id_t?language=objc
+pub const Id = enum(u64) {
+ null = 0, // OS_SIGNPOST_ID_NULL
+ invalid = 0xFFFFFFFFFFFFFFFF, // OS_SIGNPOST_ID_INVALID
+ exclusive = 0xEEEEB0B5B2B2EEEE, // OS_SIGNPOST_ID_EXCLUSIVE
+ _,
+
+ /// Generates a new signpost ID for use with signpost operations.
+ /// The ID is unique for the given log handle and can be used to track
+ /// asynchronous operations or mark specific points of interest in the code.
+ /// Returns a unique signpost ID that can be used with os_signpost functions.
+ ///
+ /// https://developer.apple.com/documentation/os/os_signpost_id_generate?language=objc
+ pub fn generate(log: *Log) Id {
+ return @enumFromInt(c.os_signpost_id_generate(@ptrCast(log)));
+ }
+
+ /// Creates a signpost ID based on a pointer value.
+ /// This is useful for tracking operations associated with a specific object
+ /// or memory location. The same pointer will always generate the same ID
+ /// for a given log handle, allowing correlation of signpost events.
+ /// Pass null to get the null signpost ID.
+ ///
+ /// https://developer.apple.com/documentation/os/os_signpost_id_for_pointer?language=objc
+ pub fn forPointer(log: *Log, ptr: ?*anyopaque) Id {
+ return @enumFromInt(c.os_signpost_id_make_with_pointer(
+ @ptrCast(log),
+ @ptrCast(ptr),
+ ));
+ }
+
+ test "generate ID" {
+ // We can't really test the return value because it may return null
+ // if signposts are disabled.
+ const id: Id = .generate(Log.create("com.mitchellh.ghostty", "test"));
+ try std.testing.expect(id != .invalid);
+ }
+
+ test "generate ID for pointer" {
+ var foo: usize = 0x1234;
+ const id: Id = .forPointer(Log.create("com.mitchellh.ghostty", "test"), &foo);
+ try std.testing.expect(id != .null);
+ }
+};
+
+/// https://developer.apple.com/documentation/os/ossignposttype?language=objc
+pub const Type = enum(u8) {
+ event = 0, // OS_SIGNPOST_EVENT
+ interval_begin = 1, // OS_SIGNPOST_INTERVAL_BEGIN
+ interval_end = 2, // OS_SIGNPOST_INTERVAL_END
+
+ pub const mask: u8 = 0x03; // OS_SIGNPOST_TYPE_MASK
+};
+
+/// Special os_log category values that surface in Instruments and other
+/// tooling.
+pub const Category = struct {
+ /// Points of Interest appear as a dedicated track in Instruments.
+ /// Use this for high-level application events that help understand
+ /// the flow of your application.
+ pub const points_of_interest: [:0]const u8 = "PointsOfInterest";
+
+ /// Dynamic Tracing category enables runtime-configurable logging.
+ /// Signposts in this category can be enabled/disabled dynamically
+ /// without recompiling.
+ pub const dynamic_tracing: [:0]const u8 = "DynamicTracing";
+
+ /// Dynamic Stack Tracing category captures call stacks at signpost
+ /// events. This provides deeper debugging information but has higher
+ /// performance overhead.
+ pub const dynamic_stack_tracing: [:0]const u8 = "DynamicStackTracing";
+};
+
+test {
+ _ = Id;
+}
+
+test enabled {
+ _ = enabled(Log.create("com.mitchellh.ghostty", "test"));
+}
+
+test "intervals" {
+ init();
+
+ const log = Log.create("com.mitchellh.ghostty", "test");
+ defer log.release();
+
+ // Test that we can begin and end an interval
+ const id = Id.generate(log);
+ intervalBegin(log, id, "Test Interval");
+}
diff --git a/vendor/libghostty-vt/pkg/macos/os/zig_macos.c b/vendor/libghostty-vt/pkg/macos/os/zig_macos.c
new file mode 100644
index 00000000..1c4f0698
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/os/zig_macos.c
@@ -0,0 +1,11 @@
+#include <os/log.h>
+#include <os/signpost.h>
+
+// A wrapper so we can use the os_log_with_type macro.
+void zig_os_log_with_type(
+ os_log_t log,
+ os_log_type_t type,
+ const char *message
+) {
+ os_log_with_type(log, type, "%{public}s", message);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text.zig b/vendor/libghostty-vt/pkg/macos/text.zig
new file mode 100644
index 00000000..bfaa388b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text.zig
@@ -0,0 +1,38 @@
+const font = @import("text/font.zig");
+const font_collection = @import("text/font_collection.zig");
+const font_descriptor = @import("text/font_descriptor.zig");
+const font_manager = @import("text/font_manager.zig");
+const frame = @import("text/frame.zig");
+const framesetter = @import("text/framesetter.zig");
+const typesetter = @import("text/typesetter.zig");
+const line = @import("text/line.zig");
+const paragraph_style = @import("text/paragraph_style.zig");
+const run = @import("text/run.zig");
+const stylized_strings = @import("text/stylized_strings.zig");
+
+pub const c = @import("text/c.zig").c;
+pub const Font = font.Font;
+pub const FontTableTag = font.FontTableTag;
+pub const FontCollection = font_collection.FontCollection;
+pub const FontDescriptor = font_descriptor.FontDescriptor;
+pub const FontAttribute = font_descriptor.FontAttribute;
+pub const FontTraitKey = font_descriptor.FontTraitKey;
+pub const FontVariationAxisKey = font_descriptor.FontVariationAxisKey;
+pub const FontSymbolicTraits = font_descriptor.FontSymbolicTraits;
+pub const createFontDescriptorsFromURL = font_manager.createFontDescriptorsFromURL;
+pub const createFontDescriptorsFromData = font_manager.createFontDescriptorsFromData;
+pub const createFontDescriptorFromData = font_manager.createFontDescriptorFromData;
+pub const Frame = frame.Frame;
+pub const Framesetter = framesetter.Framesetter;
+pub const Typesetter = typesetter.Typesetter;
+pub const Line = line.Line;
+pub const ParagraphStyle = paragraph_style.ParagraphStyle;
+pub const ParagraphStyleSetting = paragraph_style.ParagraphStyleSetting;
+pub const ParagraphStyleSpecifier = paragraph_style.ParagraphStyleSpecifier;
+pub const WritingDirection = paragraph_style.WritingDirection;
+pub const Run = run.Run;
+pub const StringAttribute = stylized_strings.StringAttribute;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/c.zig b/vendor/libghostty-vt/pkg/macos/text/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/text/ext.c b/vendor/libghostty-vt/pkg/macos/text/ext.c
new file mode 100644
index 00000000..55bff8a6
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/ext.c
@@ -0,0 +1,10 @@
+#include <CoreText/CoreText.h>
+
+// A wrapper to fix a Zig C ABI issue.
+void zig_cabi_CTLineGetBoundsWithOptions(
+ CTLineRef line,
+ CTLineBoundsOptions options,
+ CGRect *result
+) {
+ *result = CTLineGetBoundsWithOptions(line, options);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/font.zig b/vendor/libghostty-vt/pkg/macos/text/font.zig
new file mode 100644
index 00000000..ea37891f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/font.zig
@@ -0,0 +1,306 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Font = opaque {
+ pub fn createWithFontDescriptor(desc: *const text.FontDescriptor, size: f32) Allocator.Error!*Font {
+ return @as(
+ ?*Font,
+ @ptrFromInt(@intFromPtr(c.CTFontCreateWithFontDescriptor(
+ @ptrCast(desc),
+ size,
+ null,
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createForString(
+ self: *Font,
+ str: *foundation.String,
+ range: foundation.Range,
+ ) ?*Font {
+ return @ptrCast(@constCast(c.CTFontCreateForString(
+ @ptrCast(self),
+ @ptrCast(str),
+ @bitCast(range),
+ )));
+ }
+
+ pub fn copyWithAttributes(
+ self: *Font,
+ size: f32,
+ matrix: ?*const graphics.AffineTransform,
+ attrs: ?*text.FontDescriptor,
+ ) Allocator.Error!*Font {
+ return @as(
+ ?*Font,
+ @ptrFromInt(@intFromPtr(c.CTFontCreateCopyWithAttributes(
+ @ptrCast(self),
+ size,
+ @ptrCast(matrix),
+ @ptrCast(attrs),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Font) void {
+ c.CFRelease(self);
+ }
+
+ pub fn retain(self: *Font) void {
+ _ = c.CFRetain(self);
+ }
+
+ pub fn copyDescriptor(self: *Font) *text.FontDescriptor {
+ return @ptrCast(@constCast(c.CTFontCopyFontDescriptor(@ptrCast(self))));
+ }
+
+ pub fn copyFeatures(self: *Font) *foundation.Array {
+ return @ptrCast(@constCast(c.CTFontCopyFeatures(@ptrCast(self))));
+ }
+
+ pub fn copyDefaultCascadeListForLanguages(self: *Font) *foundation.Array {
+ return @ptrCast(@constCast(c.CTFontCopyDefaultCascadeListForLanguages(@ptrCast(self), null)));
+ }
+
+ pub fn copyTable(self: *Font, tag: FontTableTag) ?*foundation.Data {
+ return @ptrCast(@constCast(c.CTFontCopyTable(
+ @ptrCast(self),
+ @intFromEnum(tag),
+ c.kCTFontTableOptionNoOptions,
+ )));
+ }
+
+ pub fn getGlyphCount(self: *Font) usize {
+ return @intCast(c.CTFontGetGlyphCount(@ptrCast(self)));
+ }
+
+ pub fn getGlyphsForCharacters(self: *Font, chars: []const u16, glyphs: []graphics.Glyph) bool {
+ assert(chars.len == glyphs.len);
+ return c.CTFontGetGlyphsForCharacters(
+ @ptrCast(self),
+ chars.ptr,
+ glyphs.ptr,
+ @intCast(chars.len),
+ );
+ }
+
+ pub fn createPathForGlyph(self: *Font, glyph: graphics.Glyph) ?*graphics.Path {
+ return @ptrCast(@constCast(c.CTFontCreatePathForGlyph(
+ @ptrCast(self),
+ glyph,
+ null,
+ )));
+ }
+
+ pub fn drawGlyphs(
+ self: *Font,
+ glyphs: []const graphics.Glyph,
+ positions: []const graphics.Point,
+ context: anytype, // Must be some context type from graphics
+ ) void {
+ assert(positions.len == glyphs.len);
+ c.CTFontDrawGlyphs(
+ @ptrCast(self),
+ glyphs.ptr,
+ @ptrCast(positions.ptr),
+ glyphs.len,
+ @ptrCast(context),
+ );
+ }
+
+ pub fn getBoundingRectsForGlyphs(
+ self: *Font,
+ orientation: FontOrientation,
+ glyphs: []const graphics.Glyph,
+ rects: ?[]graphics.Rect,
+ ) graphics.Rect {
+ if (rects) |s| assert(glyphs.len == s.len);
+ return @bitCast(c.CTFontGetBoundingRectsForGlyphs(
+ @ptrCast(self),
+ @intFromEnum(orientation),
+ glyphs.ptr,
+ @ptrCast(if (rects) |s| s.ptr else null),
+ @intCast(glyphs.len),
+ ));
+ }
+
+ pub fn getAdvancesForGlyphs(
+ self: *Font,
+ orientation: FontOrientation,
+ glyphs: []const graphics.Glyph,
+ advances: ?[]graphics.Size,
+ ) f64 {
+ if (advances) |s| assert(glyphs.len == s.len);
+ return c.CTFontGetAdvancesForGlyphs(
+ @ptrCast(self),
+ @intFromEnum(orientation),
+ glyphs.ptr,
+ @ptrCast(if (advances) |s| s.ptr else null),
+ @intCast(glyphs.len),
+ );
+ }
+
+ pub fn copyAttribute(self: *Font, comptime attr: text.FontAttribute) ?attr.Value() {
+ return @ptrFromInt(@intFromPtr(c.CTFontCopyAttribute(
+ @ptrCast(self),
+ @ptrCast(attr.key()),
+ )));
+ }
+
+ pub fn copyFamilyName(self: *Font) *foundation.String {
+ return @ptrFromInt(@intFromPtr(c.CTFontCopyFamilyName(@ptrCast(self))));
+ }
+
+ pub fn copyDisplayName(self: *Font) *foundation.String {
+ return @ptrFromInt(@intFromPtr(c.CTFontCopyDisplayName(@ptrCast(self))));
+ }
+
+ pub fn copyPostScriptName(self: *Font) *foundation.String {
+ return @ptrFromInt(@intFromPtr(c.CTFontCopyPostScriptName(@ptrCast(self))));
+ }
+
+ pub fn getSymbolicTraits(self: *Font) text.FontSymbolicTraits {
+ return @bitCast(c.CTFontGetSymbolicTraits(@ptrCast(self)));
+ }
+
+ pub fn getAscent(self: *Font) f64 {
+ return c.CTFontGetAscent(@ptrCast(self));
+ }
+
+ pub fn getDescent(self: *Font) f64 {
+ return c.CTFontGetDescent(@ptrCast(self));
+ }
+
+ pub fn getLeading(self: *Font) f64 {
+ return c.CTFontGetLeading(@ptrCast(self));
+ }
+
+ pub fn getBoundingBox(self: *Font) graphics.Rect {
+ return @bitCast(c.CTFontGetBoundingBox(@ptrCast(self)));
+ }
+
+ pub fn getUnderlinePosition(self: *Font) f64 {
+ return c.CTFontGetUnderlinePosition(@ptrCast(self));
+ }
+
+ pub fn getUnderlineThickness(self: *Font) f64 {
+ return c.CTFontGetUnderlineThickness(@ptrCast(self));
+ }
+
+ pub fn getCapHeight(self: *Font) f64 {
+ return c.CTFontGetCapHeight(@ptrCast(self));
+ }
+
+ pub fn getXHeight(self: *Font) f64 {
+ return c.CTFontGetXHeight(@ptrCast(self));
+ }
+
+ pub fn getUnitsPerEm(self: *Font) u32 {
+ return c.CTFontGetUnitsPerEm(@ptrCast(self));
+ }
+
+ pub fn getSize(self: *Font) f64 {
+ return c.CTFontGetSize(@ptrCast(self));
+ }
+};
+
+pub const FontOrientation = enum(c_uint) {
+ default = c.kCTFontOrientationDefault,
+ horizontal = c.kCTFontOrientationHorizontal,
+ vertical = c.kCTFontOrientationVertical,
+};
+
+pub const FontTableTag = enum(u32) {
+ svg = c.kCTFontTableSVG,
+ os2 = c.kCTFontTableOS2,
+ head = c.kCTFontTableHead,
+ hhea = c.kCTFontTableHhea,
+ post = c.kCTFontTablePost,
+ _,
+
+ pub fn init(v: *const [4]u8) FontTableTag {
+ const raw: u32 = @bitCast(foundation.FourCharCode.init(v));
+ return @enumFromInt(raw);
+ }
+};
+
+test {
+ const testing = std.testing;
+
+ const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
+ defer name.release();
+ const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+
+ const font = try Font.createWithFontDescriptor(desc, 12);
+ defer font.release();
+
+ // Traits
+ {
+ const traits = font.getSymbolicTraits();
+ try testing.expect(!traits.color_glyphs);
+ }
+
+ var glyphs = [1]graphics.Glyph{0};
+ try testing.expect(font.getGlyphsForCharacters(
+ &[_]u16{'A'},
+ &glyphs,
+ ));
+ try testing.expect(glyphs[0] > 0);
+
+ // Bounding rect
+ {
+ var rect = font.getBoundingRectsForGlyphs(.horizontal, &glyphs, null);
+ try testing.expect(rect.size.width > 0);
+
+ var singles: [1]graphics.Rect = undefined;
+ rect = font.getBoundingRectsForGlyphs(.horizontal, &glyphs, &singles);
+ try testing.expect(rect.size.width > 0);
+ try testing.expect(singles[0].size.width > 0);
+ }
+
+ // Advances
+ {
+ var advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, null);
+ try testing.expect(advance > 0);
+
+ var singles: [1]graphics.Size = undefined;
+ advance = font.getAdvancesForGlyphs(.horizontal, &glyphs, &singles);
+ try testing.expect(advance > 0);
+ try testing.expect(singles[0].width > 0);
+ }
+
+ // Draw
+ {
+ const cs = try graphics.ColorSpace.createDeviceGray();
+ defer cs.release();
+ const ctx = try graphics.BitmapContext.create(null, 80, 80, 8, 80, cs, 0);
+ const context = graphics.BitmapContext.context;
+ defer context.release(ctx);
+
+ var pos = [_]graphics.Point{.{ .x = 0, .y = 0 }};
+ font.drawGlyphs(
+ &glyphs,
+ &pos,
+ ctx,
+ );
+ }
+}
+
+test "copy" {
+ const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
+ defer name.release();
+ const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+
+ const font = try Font.createWithFontDescriptor(desc, 12);
+ defer font.release();
+
+ const f2 = try font.copyWithAttributes(14, null, null);
+ defer f2.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/font_collection.zig b/vendor/libghostty-vt/pkg/macos/text/font_collection.zig
new file mode 100644
index 00000000..8b4a86a6
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/font_collection.zig
@@ -0,0 +1,139 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const FontCollection = opaque {
+ pub fn createFromAvailableFonts() Allocator.Error!*FontCollection {
+ return @as(
+ ?*FontCollection,
+ @ptrFromInt(@intFromPtr(c.CTFontCollectionCreateFromAvailableFonts(null))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createWithFontDescriptors(descs: *foundation.Array) Allocator.Error!*FontCollection {
+ return @as(
+ ?*FontCollection,
+ @ptrFromInt(@intFromPtr(c.CTFontCollectionCreateWithFontDescriptors(
+ @ptrCast(descs),
+ null,
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *FontCollection) void {
+ c.CFRelease(self);
+ }
+
+ pub fn createMatchingFontDescriptors(self: *FontCollection) *foundation.Array {
+ const result = c.CTFontCollectionCreateMatchingFontDescriptors(@ptrCast(self));
+ if (result) |ptr| return @ptrFromInt(@intFromPtr(ptr));
+
+ // If we have no results, we create an empty array. This is not
+ // exactly matching the Mac API. We can fix this later if we want
+ // but I chose this to make it slightly more Zig-like at the cost
+ // of some memory in the rare case.
+ return foundation.Array.create(anyopaque, &[_]*const anyopaque{}) catch unreachable;
+ }
+};
+
+fn debugDumpList(list: *foundation.Array) !void {
+ var i: usize = 0;
+ while (i < list.getCount()) : (i += 1) {
+ const desc = list.getValueAtIndex(text.FontDescriptor, i);
+ {
+ var buf: [128]u8 = undefined;
+ const name = desc.copyAttribute(.name);
+ defer name.release();
+ const cstr = name.cstring(&buf, .utf8).?;
+
+ var family_buf: [128]u8 = undefined;
+ const family = desc.copyAttribute(.family_name);
+ defer family.release();
+ const family_cstr = family.cstring(&family_buf, .utf8).?;
+
+ var buf2: [128]u8 = undefined;
+ const url = desc.copyAttribute(.url);
+ defer url.release();
+ const path = path: {
+ const blank = try foundation.String.createWithBytes("", .utf8, false);
+ defer blank.release();
+
+ const path = url.copyPath() orelse break :path "<no path>";
+ defer path.release();
+
+ const decoded = try foundation.URL.createStringByReplacingPercentEscapes(
+ path,
+ blank,
+ );
+ defer decoded.release();
+
+ break :path decoded.cstring(&buf2, .utf8) orelse
+ "<path cannot be converted to string>";
+ };
+
+ std.log.warn("i={d} name={s} family={s} path={s}", .{ i, cstr, family_cstr, path });
+ }
+ }
+}
+
+test "collection" {
+ const testing = std.testing;
+
+ const v = try FontCollection.createFromAvailableFonts();
+ defer v.release();
+
+ const list = v.createMatchingFontDescriptors();
+ defer list.release();
+
+ try testing.expect(list.getCount() > 0);
+}
+
+test "from descriptors" {
+ const testing = std.testing;
+
+ const name = try foundation.String.createWithBytes("AppleColorEmoji", .utf8, false);
+ defer name.release();
+
+ const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+
+ var desc_arr = [_]*const text.FontDescriptor{desc};
+ const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
+ defer arr.release();
+
+ const v = try FontCollection.createWithFontDescriptors(arr);
+ defer v.release();
+
+ const list = v.createMatchingFontDescriptors();
+ defer list.release();
+
+ try testing.expect(list.getCount() > 0);
+
+ // try debugDumpList(list);
+}
+
+test "from descriptors no match" {
+ const testing = std.testing;
+
+ const name = try foundation.String.createWithBytes("ThisShouldNeverExist", .utf8, false);
+ defer name.release();
+
+ const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+
+ var desc_arr = [_]*const text.FontDescriptor{desc};
+ const arr = try foundation.Array.create(text.FontDescriptor, &desc_arr);
+ defer arr.release();
+
+ const v = try FontCollection.createWithFontDescriptors(arr);
+ defer v.release();
+
+ const list = v.createMatchingFontDescriptors();
+ defer list.release();
+
+ try testing.expect(list.getCount() == 0);
+
+ //try debugDumpList(list);
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/font_descriptor.zig b/vendor/libghostty-vt/pkg/macos/text/font_descriptor.zig
new file mode 100644
index 00000000..563165bc
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/font_descriptor.zig
@@ -0,0 +1,287 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const FontDescriptor = opaque {
+ pub fn createWithNameAndSize(name: *foundation.String, size: f64) Allocator.Error!*FontDescriptor {
+ return @as(
+ ?*FontDescriptor,
+ @ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateWithNameAndSize(@ptrCast(name), size))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createWithAttributes(dict: *foundation.Dictionary) Allocator.Error!*FontDescriptor {
+ return @as(
+ ?*FontDescriptor,
+ @ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateWithAttributes(@ptrCast(dict)))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createCopyWithAttributes(
+ original: *FontDescriptor,
+ dict: *foundation.Dictionary,
+ ) Allocator.Error!*FontDescriptor {
+ return @as(
+ ?*FontDescriptor,
+ @ptrFromInt(@intFromPtr(c.CTFontDescriptorCreateCopyWithAttributes(
+ @ptrCast(original),
+ @ptrCast(dict),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn createCopyWithVariation(
+ original: *FontDescriptor,
+ id: *foundation.Number,
+ value: f64,
+ ) Allocator.Error!*FontDescriptor {
+ return @as(
+ ?*FontDescriptor,
+ @ptrCast(@constCast(c.CTFontDescriptorCreateCopyWithVariation(
+ @ptrCast(original),
+ @ptrCast(id),
+ value,
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn retain(self: *FontDescriptor) void {
+ _ = c.CFRetain(self);
+ }
+
+ pub fn release(self: *FontDescriptor) void {
+ c.CFRelease(self);
+ }
+
+ pub fn copyAttribute(self: *const FontDescriptor, comptime attr: FontAttribute) ?attr.Value() {
+ return @ptrFromInt(@intFromPtr(c.CTFontDescriptorCopyAttribute(
+ @ptrCast(self),
+ @ptrCast(attr.key()),
+ )));
+ }
+
+ pub fn copyAttributes(self: *const FontDescriptor) *foundation.Dictionary {
+ return @ptrFromInt(@intFromPtr(c.CTFontDescriptorCopyAttributes(
+ @ptrCast(self),
+ )));
+ }
+};
+
+pub const FontAttribute = enum {
+ url,
+ name,
+ display_name,
+ family_name,
+ style_name,
+ traits,
+ variation,
+ size,
+ matrix,
+ cascade_list,
+ character_set,
+ languages,
+ baseline_adjust,
+ macintosh_encodings,
+ features,
+ feature_settings,
+ fixed_advance,
+ orientation,
+ format,
+ registration_scope,
+ priority,
+ enabled,
+ downloadable,
+ downloaded,
+
+ // https://developer.apple.com/documentation/coretext/core_text_constants?language=objc
+ variation_axes,
+
+ pub fn key(self: FontAttribute) *foundation.String {
+ return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
+ .url => c.kCTFontURLAttribute,
+ .name => c.kCTFontNameAttribute,
+ .display_name => c.kCTFontDisplayNameAttribute,
+ .family_name => c.kCTFontFamilyNameAttribute,
+ .style_name => c.kCTFontStyleNameAttribute,
+ .traits => c.kCTFontTraitsAttribute,
+ .variation => c.kCTFontVariationAttribute,
+ .size => c.kCTFontSizeAttribute,
+ .matrix => c.kCTFontMatrixAttribute,
+ .cascade_list => c.kCTFontCascadeListAttribute,
+ .character_set => c.kCTFontCharacterSetAttribute,
+ .languages => c.kCTFontLanguagesAttribute,
+ .baseline_adjust => c.kCTFontBaselineAdjustAttribute,
+ .macintosh_encodings => c.kCTFontMacintoshEncodingsAttribute,
+ .features => c.kCTFontFeaturesAttribute,
+ .feature_settings => c.kCTFontFeatureSettingsAttribute,
+ .fixed_advance => c.kCTFontFixedAdvanceAttribute,
+ .orientation => c.kCTFontOrientationAttribute,
+ .format => c.kCTFontFormatAttribute,
+ .registration_scope => c.kCTFontRegistrationScopeAttribute,
+ .priority => c.kCTFontPriorityAttribute,
+ .enabled => c.kCTFontEnabledAttribute,
+ .downloadable => c.kCTFontDownloadableAttribute,
+ .downloaded => c.kCTFontDownloadedAttribute,
+ .variation_axes => c.kCTFontVariationAxesAttribute,
+ })));
+ }
+
+ pub fn Value(comptime self: FontAttribute) type {
+ return switch (self) {
+ .url => *foundation.URL,
+ .name => *foundation.String,
+ .display_name => *foundation.String,
+ .family_name => *foundation.String,
+ .style_name => *foundation.String,
+ .traits => *foundation.Dictionary,
+ .variation => *foundation.Dictionary,
+ .size => *foundation.Number,
+ .matrix => *anyopaque, // CFDataRef
+ .cascade_list => *foundation.Array,
+ .character_set => *anyopaque, // CFCharacterSetRef
+ .languages => *foundation.Array,
+ .baseline_adjust => *foundation.Number,
+ .macintosh_encodings => *foundation.Number,
+ .features => *foundation.Array,
+ .feature_settings => *foundation.Array,
+ .fixed_advance => *foundation.Number,
+ .orientation => *foundation.Number,
+ .format => *foundation.Number,
+ .registration_scope => *foundation.Number,
+ .priority => *foundation.Number,
+ .enabled => *foundation.Number,
+ .downloadable => *anyopaque, // CFBoolean
+ .downloaded => *anyopaque, // CFBoolean
+ .variation_axes => *foundation.Array,
+ };
+ }
+};
+
+pub const FontTraitKey = enum {
+ symbolic,
+ weight,
+ width,
+ slant,
+
+ pub fn key(self: FontTraitKey) *foundation.String {
+ return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
+ .symbolic => c.kCTFontSymbolicTrait,
+ .weight => c.kCTFontWeightTrait,
+ .width => c.kCTFontWidthTrait,
+ .slant => c.kCTFontSlantTrait,
+ })));
+ }
+
+ pub fn Value(self: FontTraitKey) type {
+ return switch (self) {
+ .symbolic => *foundation.Number,
+ .weight => *foundation.Number,
+ .width => *foundation.Number,
+ .slant => *foundation.Number,
+ };
+ }
+};
+
+// https://developer.apple.com/documentation/coretext/ctfont/font_variation_axis_dictionary_keys?language=objc
+pub const FontVariationAxisKey = enum {
+ identifier,
+ minimum_value,
+ maximum_value,
+ default_value,
+ name,
+ hidden,
+
+ pub fn key(self: FontVariationAxisKey) *foundation.String {
+ return @as(*foundation.String, @ptrFromInt(@intFromPtr(switch (self) {
+ .identifier => c.kCTFontVariationAxisIdentifierKey,
+ .minimum_value => c.kCTFontVariationAxisMinimumValueKey,
+ .maximum_value => c.kCTFontVariationAxisMaximumValueKey,
+ .default_value => c.kCTFontVariationAxisDefaultValueKey,
+ .name => c.kCTFontVariationAxisNameKey,
+ .hidden => c.kCTFontVariationAxisHiddenKey,
+ })));
+ }
+
+ pub fn Value(comptime self: FontVariationAxisKey) type {
+ return switch (self) {
+ .identifier => foundation.Number,
+ .minimum_value => foundation.Number,
+ .maximum_value => foundation.Number,
+ .default_value => foundation.Number,
+ .name => foundation.String,
+ .hidden => foundation.Number,
+ };
+ }
+};
+
+pub const FontSymbolicTraits = packed struct(u32) {
+ italic: bool = false,
+ bold: bool = false,
+ _unused1: u3 = 0,
+ expanded: bool = false,
+ condensed: bool = false,
+ _unused2: u3 = 0,
+ monospace: bool = false,
+ vertical: bool = false,
+ ui_optimized: bool = false,
+ color_glyphs: bool = false,
+ composite: bool = false,
+ _padding: u17 = 0,
+
+ pub fn init(num: *foundation.Number) FontSymbolicTraits {
+ var raw: i32 = undefined;
+ _ = num.getValue(.sint32, &raw);
+ return @as(FontSymbolicTraits, @bitCast(raw));
+ }
+
+ test {
+ try std.testing.expectEqual(
+ @bitSizeOf(c.CTFontSymbolicTraits),
+ @bitSizeOf(FontSymbolicTraits),
+ );
+ }
+
+ test "bitcast" {
+ const actual: c.CTFontSymbolicTraits = c.kCTFontTraitMonoSpace | c.kCTFontTraitExpanded;
+ const expected: FontSymbolicTraits = .{
+ .monospace = true,
+ .expanded = true,
+ };
+
+ try std.testing.expectEqual(actual, @as(c.CTFontSymbolicTraits, @bitCast(expected)));
+ }
+
+ test "number" {
+ const raw: i32 = c.kCTFontTraitMonoSpace | c.kCTFontTraitExpanded;
+ const num = try foundation.Number.create(.sint32, &raw);
+ defer num.release();
+
+ const expected: FontSymbolicTraits = .{ .monospace = true, .expanded = true };
+ const actual = FontSymbolicTraits.init(num);
+ try std.testing.expect(std.meta.eql(expected, actual));
+ }
+};
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
+
+test "descriptor" {
+ const testing = std.testing;
+
+ const name = try foundation.String.createWithBytes("foo", .utf8, false);
+ defer name.release();
+
+ const v = try FontDescriptor.createWithNameAndSize(name, 12);
+ defer v.release();
+
+ const copy_name = v.copyAttribute(.name).?;
+ defer copy_name.release();
+
+ {
+ var buf: [128]u8 = undefined;
+ const cstr = copy_name.cstring(&buf, .utf8).?;
+ try testing.expectEqualStrings("foo", cstr);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/font_manager.zig b/vendor/libghostty-vt/pkg/macos/text/font_manager.zig
new file mode 100644
index 00000000..988da122
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/font_manager.zig
@@ -0,0 +1,23 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const FontDescriptor = @import("./font_descriptor.zig").FontDescriptor;
+const c = @import("c.zig").c;
+
+pub fn createFontDescriptorsFromURL(url: *foundation.URL) ?*foundation.Array {
+ return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorsFromURL(
+ @ptrCast(url),
+ )));
+}
+
+pub fn createFontDescriptorsFromData(data: *foundation.Data) ?*foundation.Array {
+ return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorsFromData(
+ @ptrCast(data),
+ )));
+}
+
+pub fn createFontDescriptorFromData(data: *foundation.Data) ?*FontDescriptor {
+ return @ptrFromInt(@intFromPtr(c.CTFontManagerCreateFontDescriptorFromData(
+ @ptrCast(data),
+ )));
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/frame.zig b/vendor/libghostty-vt/pkg/macos/text/frame.zig
new file mode 100644
index 00000000..979134a1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/frame.zig
@@ -0,0 +1,33 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Frame = opaque {
+ pub fn release(self: *Frame) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getLineOrigins(
+ self: *Frame,
+ range: foundation.Range,
+ points: []graphics.Point,
+ ) void {
+ c.CTFrameGetLineOrigins(
+ @ptrCast(self),
+ @bitCast(range),
+ @ptrCast(points.ptr),
+ );
+ }
+
+ pub fn getLines(self: *Frame) *foundation.Array {
+ return @ptrFromInt(@intFromPtr(c.CTFrameGetLines(@ptrCast(self))));
+ }
+};
+
+test {
+ // See framesetter tests...
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/framesetter.zig b/vendor/libghostty-vt/pkg/macos/text/framesetter.zig
new file mode 100644
index 00000000..dbd82762
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/framesetter.zig
@@ -0,0 +1,66 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Framesetter = opaque {
+ pub fn createWithAttributedString(str: *foundation.AttributedString) Allocator.Error!*Framesetter {
+ return @as(
+ ?*Framesetter,
+ @ptrFromInt(@intFromPtr(c.CTFramesetterCreateWithAttributedString(
+ @ptrCast(str),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Framesetter) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn createFrame(
+ self: *Framesetter,
+ range: foundation.Range,
+ path: *graphics.Path,
+ attrs: ?*foundation.Dictionary,
+ ) !*text.Frame {
+ return @as(
+ ?*text.Frame,
+ @ptrFromInt(@intFromPtr(c.CTFramesetterCreateFrame(
+ @ptrCast(self),
+ @bitCast(range),
+ @ptrCast(path),
+ @ptrCast(attrs),
+ ))),
+ ) orelse error.FrameCreateFailed;
+ }
+};
+
+test {
+ const str = try foundation.MutableAttributedString.create(0);
+ defer str.release();
+ {
+ const rep = try foundation.String.createWithBytes("hello", .utf8, false);
+ defer rep.release();
+ str.replaceString(foundation.Range.init(0, 0), rep);
+ }
+
+ const fs = try Framesetter.createWithAttributedString(@ptrCast(str));
+ defer fs.release();
+
+ const path = try graphics.Path.createWithRect(graphics.Rect.init(0, 0, 100, 200), null);
+ defer path.release();
+ const frame = try fs.createFrame(
+ foundation.Range.init(0, 0),
+ path,
+ null,
+ );
+ defer frame.release();
+
+ {
+ var points: [1]graphics.Point = undefined;
+ frame.getLineOrigins(foundation.Range.init(0, 1), &points);
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/line.zig b/vendor/libghostty-vt/pkg/macos/text/line.zig
new file mode 100644
index 00000000..248f8e64
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/line.zig
@@ -0,0 +1,123 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Line = opaque {
+ pub fn createWithAttributedString(str: *foundation.AttributedString) Allocator.Error!*Line {
+ return @as(
+ ?*Line,
+ @ptrFromInt(@intFromPtr(c.CTLineCreateWithAttributedString(
+ @ptrCast(str),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Line) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getGlyphCount(self: *Line) usize {
+ return @intCast(c.CTLineGetGlyphCount(
+ @ptrCast(self),
+ ));
+ }
+
+ pub fn getBoundsWithOptions(
+ self: *Line,
+ opts: LineBoundsOptions,
+ ) graphics.Rect {
+ return @bitCast(c.CTLineGetBoundsWithOptions(
+ @ptrCast(self),
+ @bitCast(opts),
+ ));
+ }
+
+ pub fn getTypographicBounds(
+ self: *Line,
+ ascent: ?*f64,
+ descent: ?*f64,
+ leading: ?*f64,
+ ) f64 {
+ return c.CTLineGetTypographicBounds(
+ @ptrCast(self),
+ ascent,
+ descent,
+ leading,
+ );
+ }
+
+ pub fn getGlyphRuns(self: *Line) *foundation.Array {
+ return @ptrCast(@constCast(c.CTLineGetGlyphRuns(@ptrCast(self))));
+ }
+};
+
+pub const LineBoundsOptions = packed struct {
+ exclude_leading: bool = false,
+ exclude_shifts: bool = false,
+ hanging_punctuation: bool = false,
+ glyph_path_bounds: bool = false,
+ use_optical_bounds: bool = false,
+ language_extents: bool = false,
+ _padding: u58 = 0,
+
+ test {
+ try std.testing.expectEqual(
+ @bitSizeOf(c.CTLineBoundsOptions),
+ @bitSizeOf(LineBoundsOptions),
+ );
+ }
+
+ test "bitcast" {
+ const actual: c.CTLineBoundsOptions = c.kCTLineBoundsExcludeTypographicShifts |
+ c.kCTLineBoundsUseOpticalBounds;
+ const expected: LineBoundsOptions = .{
+ .exclude_shifts = true,
+ .use_optical_bounds = true,
+ };
+
+ try std.testing.expectEqual(actual, @as(c.CTLineBoundsOptions, @bitCast(expected)));
+ }
+};
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
+
+test "line" {
+ const testing = std.testing;
+
+ const font = font: {
+ const name = try foundation.String.createWithBytes("Monaco", .utf8, false);
+ defer name.release();
+ const desc = try text.FontDescriptor.createWithNameAndSize(name, 12);
+ defer desc.release();
+
+ break :font try text.Font.createWithFontDescriptor(desc, 12);
+ };
+ defer font.release();
+
+ const rep = try foundation.String.createWithBytes("hello", .utf8, false);
+ defer rep.release();
+ const str = try foundation.MutableAttributedString.create(rep.getLength());
+ defer str.release();
+ str.replaceString(foundation.Range.init(0, 0), rep);
+ str.setAttribute(
+ foundation.Range.init(0, rep.getLength()),
+ text.StringAttribute.font,
+ font,
+ );
+
+ const line = try Line.createWithAttributedString(@as(*foundation.AttributedString, @ptrCast(str)));
+ defer line.release();
+
+ try testing.expectEqual(@as(usize, 5), line.getGlyphCount());
+
+ // TODO: this is a garbage value but should work...
+ const bounds = line.getBoundsWithOptions(.{});
+ _ = bounds;
+ //std.log.warn("bounds={}", .{bounds});
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/paragraph_style.zig b/vendor/libghostty-vt/pkg/macos/text/paragraph_style.zig
new file mode 100644
index 00000000..f1d7fe95
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/paragraph_style.zig
@@ -0,0 +1,49 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+// https://developer.apple.com/documentation/coretext/ctparagraphstyle?language=objc
+pub const ParagraphStyle = opaque {
+ pub fn create(
+ settings: []const ParagraphStyleSetting,
+ ) Allocator.Error!*ParagraphStyle {
+ return @ptrCast(@constCast(c.CTParagraphStyleCreate(
+ @ptrCast(settings.ptr),
+ settings.len,
+ )));
+ }
+
+ pub fn release(self: *ParagraphStyle) void {
+ foundation.CFRelease(self);
+ }
+};
+
+/// https://developer.apple.com/documentation/coretext/ctparagraphstylesetting?language=objc
+pub const ParagraphStyleSetting = extern struct {
+ spec: ParagraphStyleSpecifier,
+ value_size: usize,
+ value: *const anyopaque,
+};
+
+/// https://developer.apple.com/documentation/coretext/ctparagraphstylespecifier?language=objc
+pub const ParagraphStyleSpecifier = enum(c_uint) {
+ base_writing_direction = 13,
+};
+
+/// https://developer.apple.com/documentation/uikit/nswritingdirectionattributename?language=objc
+pub const WritingDirection = enum(c_int) {
+ natural = -1,
+ ltr = 0,
+ rtl = 1,
+ lro = 2,
+ rlo = 3,
+};
+
+test ParagraphStyle {
+ const p = try ParagraphStyle.create(&.{});
+ defer p.release();
+}
diff --git a/vendor/libghostty-vt/pkg/macos/text/run.zig b/vendor/libghostty-vt/pkg/macos/text/run.zig
new file mode 100644
index 00000000..2895bfe3
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/run.zig
@@ -0,0 +1,117 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Run = opaque {
+ pub fn release(self: *Run) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn getGlyphCount(self: *Run) usize {
+ return @intCast(c.CTRunGetGlyphCount(@ptrCast(self)));
+ }
+
+ pub fn getGlyphsPtr(self: *Run) ?[]const graphics.Glyph {
+ const len = self.getGlyphCount();
+ if (len == 0) return &.{};
+ const ptr: [*c]const graphics.Glyph = @ptrCast(
+ c.CTRunGetGlyphsPtr(@ptrCast(self)),
+ );
+ if (ptr == null) return null;
+ return ptr[0..len];
+ }
+
+ pub fn getGlyphs(self: *Run, alloc: Allocator) ![]const graphics.Glyph {
+ const len = self.getGlyphCount();
+ const ptr = try alloc.alloc(graphics.Glyph, len);
+ errdefer alloc.free(ptr);
+ c.CTRunGetGlyphs(
+ @ptrCast(self),
+ .{ .location = 0, .length = 0 },
+ @ptrCast(ptr.ptr),
+ );
+ return ptr;
+ }
+
+ pub fn getPositionsPtr(self: *Run) ?[]const graphics.Point {
+ const len = self.getGlyphCount();
+ if (len == 0) return &.{};
+ const ptr: [*c]const graphics.Point = @ptrCast(
+ c.CTRunGetPositionsPtr(@ptrCast(self)),
+ );
+ if (ptr == null) return null;
+ return ptr[0..len];
+ }
+
+ pub fn getPositions(self: *Run, alloc: Allocator) ![]const graphics.Point {
+ const len = self.getGlyphCount();
+ const ptr = try alloc.alloc(graphics.Point, len);
+ errdefer alloc.free(ptr);
+ c.CTRunGetPositions(
+ @ptrCast(self),
+ .{ .location = 0, .length = 0 },
+ @ptrCast(ptr.ptr),
+ );
+ return ptr;
+ }
+
+ pub fn getAdvancesPtr(self: *Run) ?[]const graphics.Size {
+ const len = self.getGlyphCount();
+ if (len == 0) return &.{};
+ const ptr: [*c]const graphics.Size = @ptrCast(
+ c.CTRunGetAdvancesPtr(@ptrCast(self)),
+ );
+ if (ptr == null) return null;
+ return ptr[0..len];
+ }
+
+ pub fn getAdvances(self: *Run, alloc: Allocator) ![]const graphics.Size {
+ const len = self.getGlyphCount();
+ const ptr = try alloc.alloc(graphics.Size, len);
+ errdefer alloc.free(ptr);
+ c.CTRunGetAdvances(
+ @ptrCast(self),
+ .{ .location = 0, .length = 0 },
+ @ptrCast(ptr.ptr),
+ );
+ return ptr;
+ }
+
+ pub fn getStringIndicesPtr(self: *Run) ?[]const usize {
+ const len = self.getGlyphCount();
+ if (len == 0) return &.{};
+ const ptr: [*c]const usize = @ptrCast(
+ c.CTRunGetStringIndicesPtr(@ptrCast(self)),
+ );
+ if (ptr == null) return null;
+ return ptr[0..len];
+ }
+
+ pub fn getStringIndices(self: *Run, alloc: Allocator) ![]const usize {
+ const len = self.getGlyphCount();
+ const ptr = try alloc.alloc(usize, len);
+ errdefer alloc.free(ptr);
+ c.CTRunGetStringIndices(
+ @ptrCast(self),
+ .{ .location = 0, .length = 0 },
+ @ptrCast(ptr.ptr),
+ );
+ return ptr;
+ }
+
+ pub fn getStatus(self: *Run) Status {
+ return @bitCast(c.CTRunGetStatus(@ptrCast(self)));
+ }
+};
+
+/// https://developer.apple.com/documentation/coretext/ctrunstatus?language=objc
+pub const Status = packed struct(u32) {
+ right_to_left: bool,
+ non_monotonic: bool,
+ has_non_identity_matrix: bool,
+ _pad: u29 = 0,
+};
diff --git a/vendor/libghostty-vt/pkg/macos/text/stylized_strings.zig b/vendor/libghostty-vt/pkg/macos/text/stylized_strings.zig
new file mode 100644
index 00000000..8e262f9a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/stylized_strings.zig
@@ -0,0 +1,16 @@
+const foundation = @import("../foundation.zig");
+const c = @import("c.zig").c;
+
+pub const StringAttribute = enum {
+ font,
+ paragraph_style,
+ writing_direction,
+
+ pub fn key(self: StringAttribute) *foundation.String {
+ return @ptrFromInt(@intFromPtr(switch (self) {
+ .font => c.kCTFontAttributeName,
+ .paragraph_style => c.kCTParagraphStyleAttributeName,
+ .writing_direction => c.kCTWritingDirectionAttributeName,
+ }));
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/text/typesetter.zig b/vendor/libghostty-vt/pkg/macos/text/typesetter.zig
new file mode 100644
index 00000000..dc07df98
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/text/typesetter.zig
@@ -0,0 +1,36 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const foundation = @import("../foundation.zig");
+const graphics = @import("../graphics.zig");
+const text = @import("../text.zig");
+const c = @import("c.zig").c;
+
+pub const Typesetter = opaque {
+ pub fn createWithAttributedStringAndOptions(
+ str: *foundation.AttributedString,
+ opts: *foundation.Dictionary,
+ ) Allocator.Error!*Typesetter {
+ return @as(
+ ?*Typesetter,
+ @ptrFromInt(@intFromPtr(c.CTTypesetterCreateWithAttributedStringAndOptions(
+ @ptrCast(str),
+ @ptrCast(opts),
+ ))),
+ ) orelse Allocator.Error.OutOfMemory;
+ }
+
+ pub fn release(self: *Typesetter) void {
+ foundation.CFRelease(self);
+ }
+
+ pub fn createLine(
+ self: *Typesetter,
+ range: foundation.c.CFRange,
+ ) *text.Line {
+ return @ptrFromInt(@intFromPtr(c.CTTypesetterCreateLine(
+ @ptrCast(self),
+ range,
+ )));
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/video.zig b/vendor/libghostty-vt/pkg/macos/video.zig
new file mode 100644
index 00000000..d0b1125a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/video.zig
@@ -0,0 +1,10 @@
+const display_link = @import("video/display_link.zig");
+const pixel_format = @import("video/pixel_format.zig");
+
+pub const c = @import("video/c.zig").c;
+pub const DisplayLink = display_link.DisplayLink;
+pub const PixelFormat = pixel_format.PixelFormat;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/macos/video/c.zig b/vendor/libghostty-vt/pkg/macos/video/c.zig
new file mode 100644
index 00000000..1a7d1627
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/video/c.zig
@@ -0,0 +1 @@
+pub const c = @import("../main.zig").c;
diff --git a/vendor/libghostty-vt/pkg/macos/video/display_link.zig b/vendor/libghostty-vt/pkg/macos/video/display_link.zig
new file mode 100644
index 00000000..7d6b437f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/video/display_link.zig
@@ -0,0 +1,86 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+
+pub const DisplayLink = opaque {
+ pub const Error = error{
+ InvalidOperation,
+ };
+
+ pub fn createWithActiveCGDisplays() Allocator.Error!*DisplayLink {
+ var result: ?*DisplayLink = null;
+ if (c.CVDisplayLinkCreateWithActiveCGDisplays(
+ @ptrCast(&result),
+ ) != c.kCVReturnSuccess)
+ return error.OutOfMemory;
+
+ return result orelse error.OutOfMemory;
+ }
+
+ pub fn release(self: *DisplayLink) void {
+ c.CVDisplayLinkRelease(@ptrCast(self));
+ }
+
+ pub fn start(self: *DisplayLink) Error!void {
+ if (c.CVDisplayLinkStart(@ptrCast(self)) != c.kCVReturnSuccess)
+ return error.InvalidOperation;
+ }
+
+ pub fn stop(self: *DisplayLink) Error!void {
+ if (c.CVDisplayLinkStop(@ptrCast(self)) != c.kCVReturnSuccess)
+ return error.InvalidOperation;
+ }
+
+ pub fn isRunning(self: *DisplayLink) bool {
+ return c.CVDisplayLinkIsRunning(@ptrCast(self)) != 0;
+ }
+
+ pub fn setCurrentCGDisplay(
+ self: *DisplayLink,
+ display_id: c.CGDirectDisplayID,
+ ) Error!void {
+ if (c.CVDisplayLinkSetCurrentCGDisplay(
+ @ptrCast(self),
+ display_id,
+ ) != c.kCVReturnSuccess)
+ return error.InvalidOperation;
+ }
+
+ // Note: this purposely throws away a ton of arguments I didn't need.
+ // It would be trivial to refactor this into Zig types and properly
+ // pass this through.
+ pub fn setOutputCallback(
+ self: *DisplayLink,
+ comptime Userdata: type,
+ comptime callbackFn: *const fn (*DisplayLink, ?*Userdata) void,
+ userinfo: ?*Userdata,
+ ) Error!void {
+ if (c.CVDisplayLinkSetOutputCallback(
+ @ptrCast(self),
+ @ptrCast(&(struct {
+ fn callback(
+ displayLink: *DisplayLink,
+ inNow: *const c.CVTimeStamp,
+ inOutputTime: *const c.CVTimeStamp,
+ flagsIn: c.CVOptionFlags,
+ flagsOut: *c.CVOptionFlags,
+ inner_userinfo: ?*anyopaque,
+ ) callconv(.c) c.CVReturn {
+ _ = inNow;
+ _ = inOutputTime;
+ _ = flagsIn;
+ _ = flagsOut;
+
+ callbackFn(
+ displayLink,
+ @ptrCast(@alignCast(inner_userinfo)),
+ );
+ return c.kCVReturnSuccess;
+ }
+ }).callback),
+ userinfo,
+ ) != c.kCVReturnSuccess)
+ return error.InvalidOperation;
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/macos/video/pixel_format.zig b/vendor/libghostty-vt/pkg/macos/video/pixel_format.zig
new file mode 100644
index 00000000..78091daa
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/macos/video/pixel_format.zig
@@ -0,0 +1,171 @@
+const c = @import("c.zig").c;
+
+pub const PixelFormat = enum(c_int) {
+ /// 1 bit indexed
+ @"1Monochrome" = c.kCVPixelFormatType_1Monochrome,
+ /// 2 bit indexed
+ @"2Indexed" = c.kCVPixelFormatType_2Indexed,
+ /// 4 bit indexed
+ @"4Indexed" = c.kCVPixelFormatType_4Indexed,
+ /// 8 bit indexed
+ @"8Indexed" = c.kCVPixelFormatType_8Indexed,
+ /// 1 bit indexed gray, white is zero
+ @"1IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_1IndexedGray_WhiteIsZero,
+ /// 2 bit indexed gray, white is zero
+ @"2IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_2IndexedGray_WhiteIsZero,
+ /// 4 bit indexed gray, white is zero
+ @"4IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_4IndexedGray_WhiteIsZero,
+ /// 8 bit indexed gray, white is zero
+ @"8IndexedGray_WhiteIsZero" = c.kCVPixelFormatType_8IndexedGray_WhiteIsZero,
+ /// 16 bit BE RGB 555
+ @"16BE555" = c.kCVPixelFormatType_16BE555,
+ /// 16 bit LE RGB 555
+ @"16LE555" = c.kCVPixelFormatType_16LE555,
+ /// 16 bit LE RGB 5551
+ @"16LE5551" = c.kCVPixelFormatType_16LE5551,
+ /// 16 bit BE RGB 565
+ @"16BE565" = c.kCVPixelFormatType_16BE565,
+ /// 16 bit LE RGB 565
+ @"16LE565" = c.kCVPixelFormatType_16LE565,
+ /// 24 bit RGB
+ @"24RGB" = c.kCVPixelFormatType_24RGB,
+ /// 24 bit BGR
+ @"24BGR" = c.kCVPixelFormatType_24BGR,
+ /// 32 bit ARGB
+ @"32ARGB" = c.kCVPixelFormatType_32ARGB,
+ /// 32 bit BGRA
+ @"32BGRA" = c.kCVPixelFormatType_32BGRA,
+ /// 32 bit ABGR
+ @"32ABGR" = c.kCVPixelFormatType_32ABGR,
+ /// 32 bit RGBA
+ @"32RGBA" = c.kCVPixelFormatType_32RGBA,
+ /// 64 bit ARGB, 16-bit big-endian samples
+ @"64ARGB" = c.kCVPixelFormatType_64ARGB,
+ /// 64 bit RGBA, 16-bit little-endian full-range (0-65535) samples
+ @"64RGBALE" = c.kCVPixelFormatType_64RGBALE,
+ /// 48 bit RGB, 16-bit big-endian samples
+ @"48RGB" = c.kCVPixelFormatType_48RGB,
+ /// 32 bit AlphaGray, 16-bit big-endian samples, black is zero
+ @"32AlphaGray" = c.kCVPixelFormatType_32AlphaGray,
+ /// 16 bit Grayscale, 16-bit big-endian samples, black is zero
+ @"16Gray" = c.kCVPixelFormatType_16Gray,
+ /// 30 bit RGB, 10-bit big-endian samples, 2 unused padding bits (at least significant end).
+ @"30RGB" = c.kCVPixelFormatType_30RGB,
+ /// 30 bit RGB, 10-bit big-endian samples, 2 unused padding bits (at most significant end), video-range (64-940).
+ @"30RGB_r210" = c.kCVPixelFormatType_30RGB_r210,
+ /// Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1
+ @"422YpCbCr8" = c.kCVPixelFormatType_422YpCbCr8,
+ /// Component Y'CbCrA 8-bit 4:4:4:4, ordered Cb Y' Cr A
+ @"4444YpCbCrA8" = c.kCVPixelFormatType_4444YpCbCrA8,
+ /// Component Y'CbCrA 8-bit 4:4:4:4, rendering format. full range alpha, zero biased YUV, ordered A Y' Cb Cr
+ @"4444YpCbCrA8R" = c.kCVPixelFormatType_4444YpCbCrA8R,
+ /// Component Y'CbCrA 8-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr.
+ @"4444AYpCbCr8" = c.kCVPixelFormatType_4444AYpCbCr8,
+ /// Component Y'CbCrA 16-bit 4:4:4:4, ordered A Y' Cb Cr, full range alpha, video range Y'CbCr, 16-bit little-endian samples.
+ @"4444AYpCbCr16" = c.kCVPixelFormatType_4444AYpCbCr16,
+ /// Component AY'CbCr single precision floating-point 4:4:4:4
+ @"4444AYpCbCrFloat" = c.kCVPixelFormatType_4444AYpCbCrFloat,
+ /// Component Y'CbCr 8-bit 4:4:4, ordered Cr Y' Cb, video range Y'CbCr
+ @"444YpCbCr8" = c.kCVPixelFormatType_444YpCbCr8,
+ /// Component Y'CbCr 10,12,14,16-bit 4:2:2
+ @"422YpCbCr16" = c.kCVPixelFormatType_422YpCbCr16,
+ /// Component Y'CbCr 10-bit 4:2:2
+ @"422YpCbCr10" = c.kCVPixelFormatType_422YpCbCr10,
+ /// Component Y'CbCr 10-bit 4:4:4
+ @"444YpCbCr10" = c.kCVPixelFormatType_444YpCbCr10,
+ /// Planar Component Y'CbCr 8-bit 4:2:0. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct
+ @"420YpCbCr8Planar" = c.kCVPixelFormatType_420YpCbCr8Planar,
+ /// Planar Component Y'CbCr 8-bit 4:2:0, full range. baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrPlanar struct
+ @"420YpCbCr8PlanarFullRange" = c.kCVPixelFormatType_420YpCbCr8PlanarFullRange,
+ /// First plane: Video-range Component Y'CbCr 8-bit 4:2:2, ordered Cb Y'0 Cr Y'1; second plane: alpha 8-bit 0-255
+ @"422YpCbCr_4A_8BiPlanar" = c.kCVPixelFormatType_422YpCbCr_4A_8BiPlanar,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:2:0, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"420YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:2:0, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"420YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_420YpCbCr8BiPlanarFullRange,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:2:2, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"422YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr8BiPlanarVideoRange,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:2:2, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"422YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_422YpCbCr8BiPlanarFullRange,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:4:4, video-range (luma=[16,235] chroma=[16,240]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"444YpCbCr8BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr8BiPlanarVideoRange,
+ /// Bi-Planar Component Y'CbCr 8-bit 4:4:4, full-range (luma=[0,255] chroma=[1,255]). baseAddr points to a big-endian CVPlanarPixelBufferInfo_YCbCrBiPlanar struct
+ @"444YpCbCr8BiPlanarFullRange" = c.kCVPixelFormatType_444YpCbCr8BiPlanarFullRange,
+ /// Component Y'CbCr 8-bit 4:2:2, ordered Y'0 Cb Y'1 Cr
+ @"422YpCbCr8_yuvs" = c.kCVPixelFormatType_422YpCbCr8_yuvs,
+ /// Component Y'CbCr 8-bit 4:2:2, full range, ordered Y'0 Cb Y'1 Cr
+ @"422YpCbCr8FullRange" = c.kCVPixelFormatType_422YpCbCr8FullRange,
+ /// 8 bit one component, black is zero
+ OneComponent8 = c.kCVPixelFormatType_OneComponent8,
+ /// 8 bit two component, black is zero
+ TwoComponent8 = c.kCVPixelFormatType_TwoComponent8,
+ /// little-endian RGB101010, 2 MSB are ignored, wide-gamut (384-895)
+ @"30RGBLEPackedWideGamut" = c.kCVPixelFormatType_30RGBLEPackedWideGamut,
+ /// little-endian ARGB2101010 full-range ARGB
+ ARGB2101010LEPacked = c.kCVPixelFormatType_ARGB2101010LEPacked,
+ /// little-endian ARGB10101010, each 10 bits in the MSBs of 16bits, wide-gamut (384-895, including alpha)
+ @"40ARGBLEWideGamut" = c.kCVPixelFormatType_40ARGBLEWideGamut,
+ /// little-endian ARGB10101010, each 10 bits in the MSBs of 16bits, wide-gamut (384-895, including alpha). Alpha premultiplied
+ @"40ARGBLEWideGamutPremultiplied" = c.kCVPixelFormatType_40ARGBLEWideGamutPremultiplied,
+ /// 10 bit little-endian one component, stored as 10 MSBs of 16 bits, black is zero
+ OneComponent10 = c.kCVPixelFormatType_OneComponent10,
+ /// 12 bit little-endian one component, stored as 12 MSBs of 16 bits, black is zero
+ OneComponent12 = c.kCVPixelFormatType_OneComponent12,
+ /// 16 bit little-endian one component, black is zero
+ OneComponent16 = c.kCVPixelFormatType_OneComponent16,
+ /// 16 bit little-endian two component, black is zero
+ TwoComponent16 = c.kCVPixelFormatType_TwoComponent16,
+ /// 16 bit one component IEEE half-precision float, 16-bit little-endian samples
+ OneComponent16Half = c.kCVPixelFormatType_OneComponent16Half,
+ /// 32 bit one component IEEE float, 32-bit little-endian samples
+ OneComponent32Float = c.kCVPixelFormatType_OneComponent32Float,
+ /// 16 bit two component IEEE half-precision float, 16-bit little-endian samples
+ TwoComponent16Half = c.kCVPixelFormatType_TwoComponent16Half,
+ /// 32 bit two component IEEE float, 32-bit little-endian samples
+ TwoComponent32Float = c.kCVPixelFormatType_TwoComponent32Float,
+ /// 64 bit RGBA IEEE half-precision float, 16-bit little-endian samples
+ @"64RGBAHalf" = c.kCVPixelFormatType_64RGBAHalf,
+ /// 128 bit RGBA IEEE float, 32-bit little-endian samples
+ @"128RGBAFloat" = c.kCVPixelFormatType_128RGBAFloat,
+ /// Bayer 14-bit Little-Endian, packed in 16-bits, ordered G R G R... alternating with B G B G...
+ @"14Bayer_GRBG" = c.kCVPixelFormatType_14Bayer_GRBG,
+ /// Bayer 14-bit Little-Endian, packed in 16-bits, ordered R G R G... alternating with G B G B...
+ @"14Bayer_RGGB" = c.kCVPixelFormatType_14Bayer_RGGB,
+ /// Bayer 14-bit Little-Endian, packed in 16-bits, ordered B G B G... alternating with G R G R...
+ @"14Bayer_BGGR" = c.kCVPixelFormatType_14Bayer_BGGR,
+ /// Bayer 14-bit Little-Endian, packed in 16-bits, ordered G B G B... alternating with R G R G...
+ @"14Bayer_GBRG" = c.kCVPixelFormatType_14Bayer_GBRG,
+ /// IEEE754-2008 binary16 (half float), describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) )
+ DisparityFloat16 = c.kCVPixelFormatType_DisparityFloat16,
+ /// IEEE754-2008 binary32 float, describing the normalized shift when comparing two images. Units are 1/meters: ( pixelShift / (pixelFocalLength * baselineInMeters) )
+ DisparityFloat32 = c.kCVPixelFormatType_DisparityFloat32,
+ /// IEEE754-2008 binary16 (half float), describing the depth (distance to an object) in meters
+ DepthFloat16 = c.kCVPixelFormatType_DepthFloat16,
+ /// IEEE754-2008 binary32 float, describing the depth (distance to an object) in meters
+ DepthFloat32 = c.kCVPixelFormatType_DepthFloat32,
+ /// 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
+ @"420YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_420YpCbCr10BiPlanarVideoRange,
+ /// 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
+ @"422YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr10BiPlanarVideoRange,
+ /// 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, video-range (luma=[64,940] chroma=[64,960])
+ @"444YpCbCr10BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr10BiPlanarVideoRange,
+ /// 2 plane YCbCr10 4:2:0, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
+ @"420YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_420YpCbCr10BiPlanarFullRange,
+ /// 2 plane YCbCr10 4:2:2, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
+ @"422YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_422YpCbCr10BiPlanarFullRange,
+ /// 2 plane YCbCr10 4:4:4, each 10 bits in the MSBs of 16bits, full-range (Y range 0-1023)
+ @"444YpCbCr10BiPlanarFullRange" = c.kCVPixelFormatType_444YpCbCr10BiPlanarFullRange,
+ /// first and second planes as per 420YpCbCr8BiPlanarVideoRange (420v), alpha 8 bits in third plane full-range. No CVPlanarPixelBufferInfo struct.
+ @"420YpCbCr8VideoRange_8A_TriPlanar" = c.kCVPixelFormatType_420YpCbCr8VideoRange_8A_TriPlanar,
+ /// Single plane Bayer 16-bit little-endian sensor element ("sensel".*) samples from full-size decoding of ProRes RAW images; Bayer pattern (sensel ordering) and other raw conversion information is described via buffer attachments
+ @"16VersatileBayer" = c.kCVPixelFormatType_16VersatileBayer,
+ /// Single plane 64-bit RGBA (16-bit little-endian samples) from downscaled decoding of ProRes RAW images; components--which may not be co-sited with one another--are sensel values and require raw conversion, information for which is described via buffer attachments
+ @"64RGBA_DownscaledProResRAW" = c.kCVPixelFormatType_64RGBA_DownscaledProResRAW,
+ /// 2 plane YCbCr16 4:2:2, video-range (luma=[4096,60160] chroma=[4096,61440])
+ @"422YpCbCr16BiPlanarVideoRange" = c.kCVPixelFormatType_422YpCbCr16BiPlanarVideoRange,
+ /// 2 plane YCbCr16 4:4:4, video-range (luma=[4096,60160] chroma=[4096,61440])
+ @"444YpCbCr16BiPlanarVideoRange" = c.kCVPixelFormatType_444YpCbCr16BiPlanarVideoRange,
+ /// 3 plane video-range YCbCr16 4:4:4 with 16-bit full-range alpha (luma=[4096,60160] chroma=[4096,61440] alpha=[0,65535]). No CVPlanarPixelBufferInfo struct.
+ @"444YpCbCr16VideoRange_16A_TriPlanar" = c.kCVPixelFormatType_444YpCbCr16VideoRange_16A_TriPlanar,
+ _,
+};
diff --git a/vendor/libghostty-vt/pkg/oniguruma/build.zig b/vendor/libghostty-vt/pkg/oniguruma/build.zig
new file mode 100644
index 00000000..efc013b4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/build.zig
@@ -0,0 +1,171 @@
+const std = @import("std");
+const NativeTargetInfo = std.zig.system.NativeTargetInfo;
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const module = b.addModule("oniguruma", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ var test_exe: ?*std.Build.Step.Compile = null;
+ if (target.query.isNative()) {
+ test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ const tests_run = b.addRunArtifact(test_exe.?);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+
+ // Uncomment this if we're debugging tests
+ b.installArtifact(test_exe.?);
+ }
+
+ if (b.systemIntegrationOption("oniguruma", .{})) {
+ module.linkSystemLibrary("oniguruma", dynamic_link_opts);
+
+ if (test_exe) |exe| {
+ exe.linkSystemLibrary2("oniguruma", dynamic_link_opts);
+ }
+ } else {
+ const lib = try buildLib(b, module, .{
+ .target = target,
+ .optimize = optimize,
+ });
+
+ if (test_exe) |exe| {
+ exe.linkLibrary(lib);
+ }
+ }
+}
+
+fn buildLib(b: *std.Build, module: *std.Build.Module, options: anytype) !*std.Build.Step.Compile {
+ const target = options.target;
+ const optimize = options.optimize;
+
+ const lib = b.addLibrary(.{
+ .name = "oniguruma",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ const t = target.result;
+ const is_windows = t.os.tag == .windows;
+ lib.linkLibC();
+
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (b.lazyDependency("oniguruma", .{})) |upstream| {
+ lib.addIncludePath(upstream.path("src"));
+ module.addIncludePath(upstream.path("src"));
+
+ lib.addConfigHeader(b.addConfigHeader(.{
+ .style = .{ .cmake = upstream.path("src/config.h.cmake.in") },
+ }, .{
+ .PACKAGE = "oniguruma",
+ .PACKAGE_VERSION = "6.9.9",
+ .VERSION = "6.9.9",
+ .HAVE_ALLOCA = true,
+ .HAVE_ALLOCA_H = !is_windows,
+ .USE_CRNL_AS_LINE_TERMINATOR = is_windows,
+ .HAVE_STDINT_H = true,
+ .HAVE_SYS_TIMES_H = !is_windows,
+ .HAVE_SYS_TIME_H = !is_windows,
+ .HAVE_SYS_TYPES_H = true,
+ .HAVE_UNISTD_H = !is_windows,
+ .HAVE_INTTYPES_H = true,
+ .SIZEOF_INT = t.cTypeByteSize(.int),
+ .SIZEOF_LONG = t.cTypeByteSize(.long),
+ .SIZEOF_LONG_LONG = t.cTypeByteSize(.longlong),
+ .SIZEOF_VOIDP = t.ptrBitWidth() / t.cTypeBitSize(.char),
+ }));
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ "src/regerror.c",
+ "src/regparse.c",
+ "src/regext.c",
+ "src/regcomp.c",
+ "src/regexec.c",
+ "src/reggnu.c",
+ "src/regenc.c",
+ "src/regsyntax.c",
+ "src/regtrav.c",
+ "src/regversion.c",
+ "src/st.c",
+ "src/onig_init.c",
+ "src/unicode.c",
+ "src/ascii.c",
+ "src/utf8.c",
+ "src/utf16_be.c",
+ "src/utf16_le.c",
+ "src/utf32_be.c",
+ "src/utf32_le.c",
+ "src/euc_jp.c",
+ "src/sjis.c",
+ "src/iso8859_1.c",
+ "src/iso8859_2.c",
+ "src/iso8859_3.c",
+ "src/iso8859_4.c",
+ "src/iso8859_5.c",
+ "src/iso8859_6.c",
+ "src/iso8859_7.c",
+ "src/iso8859_8.c",
+ "src/iso8859_9.c",
+ "src/iso8859_10.c",
+ "src/iso8859_11.c",
+ "src/iso8859_13.c",
+ "src/iso8859_14.c",
+ "src/iso8859_15.c",
+ "src/iso8859_16.c",
+ "src/euc_tw.c",
+ "src/euc_kr.c",
+ "src/big5.c",
+ "src/gb18030.c",
+ "src/koi8_r.c",
+ "src/cp1251.c",
+ "src/euc_jp_prop.c",
+ "src/sjis_prop.c",
+ "src/unicode_unfold_key.c",
+ "src/unicode_fold1_key.c",
+ "src/unicode_fold2_key.c",
+ "src/unicode_fold3_key.c",
+ },
+ });
+
+ lib.installHeadersDirectory(
+ upstream.path("src"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ return lib;
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/build.zig.zon b/vendor/libghostty-vt/pkg/oniguruma/build.zig.zon
new file mode 100644
index 00000000..10ec786c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/build.zig.zon
@@ -0,0 +1,16 @@
+.{
+ .name = .oniguruma,
+ .version = "6.9.9",
+ .fingerprint = 0xe3b537b18c5785a8,
+ .paths = .{""},
+ .dependencies = .{
+ // kkos/oniguruma
+ .oniguruma = .{
+ .url = "https://deps.files.ghostty.org/oniguruma-1220c15e72eadd0d9085a8af134904d9a0f5dfcbed5f606ad60edc60ebeccd9706bb.tar.gz",
+ .hash = "N-V-__8AAHjwMQDBXnLq3Q2QhaivE0kE2aD138vtX2Bq1g7c",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/c.zig b/vendor/libghostty-vt/pkg/oniguruma/c.zig
new file mode 100644
index 00000000..73732f90
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("oniguruma.h");
+});
diff --git a/vendor/libghostty-vt/pkg/oniguruma/errors.zig b/vendor/libghostty-vt/pkg/oniguruma/errors.zig
new file mode 100644
index 00000000..d63a481b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/errors.zig
@@ -0,0 +1,201 @@
+const c = @import("c.zig").c;
+const Encoding = @import("types.zig").Encoding;
+
+/// Maximum error message length.
+pub const MAX_ERROR_LEN = c.ONIG_MAX_ERROR_MESSAGE_LEN;
+
+/// Convert an Oniguruma error to an error.
+pub fn convertError(code: c_int) !c_int {
+ if (code >= 0) return code;
+ inline for (error_code_map) |m| {
+ if (m[1] == code) return m[0];
+ }
+
+ return Error.Unknown;
+}
+
+/// Convert an error code to a string. buf must be at least
+/// MAX_ERROR_LEN bytes long.
+pub fn errorString(buf: []u8, code: c_int) ![]u8 {
+ const len = c.onig_error_code_to_str(buf.ptr, code);
+ return buf[0..@intCast(len)];
+}
+
+/// The Oniguruma error info type, matching the C type exactly.
+pub const ErrorInfo = extern struct {
+ encoding: *Encoding,
+ par: [*]u8,
+ par_end: [*]u8,
+};
+
+/// All possible Oniguruma errors.
+pub const Error = error{
+ Mismatch,
+ NoSupportConfig,
+ Abort,
+ Memory,
+ TypeBug,
+ ParserBug,
+ StackBug,
+ UndefinedBytecode,
+ UnexpectedBytecode,
+ MatchStackLimitOver,
+ ParseDepthLimitOver,
+ RetryLimitInMatchOver,
+ RetryLimitInSearchOver,
+ SubexpCallLimitInSearchOver,
+ DefaultEncodingIsNotSet,
+ SpecifiedEncodingCantConvertToWideChar,
+ FailToInitialize,
+ InvalidArgument,
+ EndPatternAtLeftBrace,
+ EndPatternAtLeftBracket,
+ EmptyCharClass,
+ PrematureEndOfCharClass,
+ EndPatternAtEscape,
+ EndPatternAtMeta,
+ EndPatternAtControl,
+ MetaCodeSyntax,
+ ControlCodeSyntax,
+ CharClassValueAtEndOfRange,
+ CharClassValueAtStartOfRange,
+ UnmatchedRangeSpecifierInCharClass,
+ TargetOfRepeatOperatorNotSpecified,
+ TargetOfRepeatOperatorInvalid,
+ NestedRepeatOperator,
+ UnmatchedCloseParenthesis,
+ EndPatternWithUnmatchedParenthesis,
+ EndPatternInGroup,
+ UndefinedGroupOption,
+ InvalidGroupOption,
+ InvalidPosixBracketType,
+ InvalidLookBehindPattern,
+ InvalidRepeatRangePattern,
+ TooBigNumber,
+ TooBigNumberForRepeatRange,
+ UpperSmallerThanLowerInRepeatRange,
+ EmptyRangeInCharClass,
+ MismatchCodeLengthInClassRange,
+ TooManyMultiByteRanges,
+ TooShortMultiByteString,
+ TooBigBackrefNumber,
+ InvalidBackref,
+ NumberedBackrefOrCallNotAllowed,
+ TooManyCaptures,
+ TooLongWideCharValue,
+ UndefinedOperator,
+ EmptyGroupName,
+ InvalidGroupName,
+ InvalidCharInGroupName,
+ UndefinedNameReference,
+ UndefinedGroupReference,
+ MultiplexDefinedName,
+ MultiplexDefinitionNameCall,
+ NeverEndingRecursion,
+ GroupNumberOverForCaptureHistory,
+ InvalidCharPropertyName,
+ InvalidIfElseSyntax,
+ InvalidAbsentGroupPattern,
+ InvalidAbsentGroupGeneratorPattern,
+ InvalidCalloutPattern,
+ InvalidCalloutName,
+ UndefinedCalloutName,
+ InvalidCalloutBody,
+ InvalidCalloutTagName,
+ InvalidCalloutArg,
+ InvalidCodePointValue,
+ InvalidWideCharValue,
+ TooBigWideCharValue,
+ NotSupportedEncodingCombination,
+ InvalidCombinationOfOptions,
+ TooManyUserDefinedObjects,
+ TooLongPropertyName,
+ VeryInefficientPattern,
+ LibraryIsNotInitialized,
+ Unknown,
+};
+
+const error_code_map: []const struct { Error, c_int } = &.{
+ .{ Error.Mismatch, c.ONIG_MISMATCH },
+ .{ Error.NoSupportConfig, c.ONIG_NO_SUPPORT_CONFIG },
+ .{ Error.Abort, c.ONIG_ABORT },
+ .{ Error.Memory, c.ONIGERR_MEMORY },
+ .{ Error.TypeBug, c.ONIGERR_TYPE_BUG },
+ .{ Error.ParserBug, c.ONIGERR_PARSER_BUG },
+ .{ Error.StackBug, c.ONIGERR_STACK_BUG },
+ .{ Error.UndefinedBytecode, c.ONIGERR_UNDEFINED_BYTECODE },
+ .{ Error.UnexpectedBytecode, c.ONIGERR_UNEXPECTED_BYTECODE },
+ .{ Error.MatchStackLimitOver, c.ONIGERR_MATCH_STACK_LIMIT_OVER },
+ .{ Error.ParseDepthLimitOver, c.ONIGERR_PARSE_DEPTH_LIMIT_OVER },
+ .{ Error.RetryLimitInMatchOver, c.ONIGERR_RETRY_LIMIT_IN_MATCH_OVER },
+ .{ Error.RetryLimitInSearchOver, c.ONIGERR_RETRY_LIMIT_IN_SEARCH_OVER },
+ .{ Error.SubexpCallLimitInSearchOver, c.ONIGERR_SUBEXP_CALL_LIMIT_IN_SEARCH_OVER },
+ .{ Error.DefaultEncodingIsNotSet, c.ONIGERR_DEFAULT_ENCODING_IS_NOT_SET },
+ .{ Error.SpecifiedEncodingCantConvertToWideChar, c.ONIGERR_SPECIFIED_ENCODING_CANT_CONVERT_TO_WIDE_CHAR },
+ .{ Error.FailToInitialize, c.ONIGERR_FAIL_TO_INITIALIZE },
+ .{ Error.InvalidArgument, c.ONIGERR_INVALID_ARGUMENT },
+ .{ Error.EndPatternAtLeftBrace, c.ONIGERR_END_PATTERN_AT_LEFT_BRACE },
+ .{ Error.EndPatternAtLeftBracket, c.ONIGERR_END_PATTERN_AT_LEFT_BRACKET },
+ .{ Error.EmptyCharClass, c.ONIGERR_EMPTY_CHAR_CLASS },
+ .{ Error.PrematureEndOfCharClass, c.ONIGERR_PREMATURE_END_OF_CHAR_CLASS },
+ .{ Error.EndPatternAtEscape, c.ONIGERR_END_PATTERN_AT_ESCAPE },
+ .{ Error.EndPatternAtMeta, c.ONIGERR_END_PATTERN_AT_META },
+ .{ Error.EndPatternAtControl, c.ONIGERR_END_PATTERN_AT_CONTROL },
+ .{ Error.MetaCodeSyntax, c.ONIGERR_META_CODE_SYNTAX },
+ .{ Error.ControlCodeSyntax, c.ONIGERR_CONTROL_CODE_SYNTAX },
+ .{ Error.CharClassValueAtEndOfRange, c.ONIGERR_CHAR_CLASS_VALUE_AT_END_OF_RANGE },
+ .{ Error.CharClassValueAtStartOfRange, c.ONIGERR_CHAR_CLASS_VALUE_AT_START_OF_RANGE },
+ .{ Error.UnmatchedRangeSpecifierInCharClass, c.ONIGERR_UNMATCHED_RANGE_SPECIFIER_IN_CHAR_CLASS },
+ .{ Error.TargetOfRepeatOperatorNotSpecified, c.ONIGERR_TARGET_OF_REPEAT_OPERATOR_NOT_SPECIFIED },
+ .{ Error.TargetOfRepeatOperatorInvalid, c.ONIGERR_TARGET_OF_REPEAT_OPERATOR_INVALID },
+ .{ Error.NestedRepeatOperator, c.ONIGERR_NESTED_REPEAT_OPERATOR },
+ .{ Error.UnmatchedCloseParenthesis, c.ONIGERR_UNMATCHED_CLOSE_PARENTHESIS },
+ .{ Error.EndPatternWithUnmatchedParenthesis, c.ONIGERR_END_PATTERN_WITH_UNMATCHED_PARENTHESIS },
+ .{ Error.EndPatternInGroup, c.ONIGERR_END_PATTERN_IN_GROUP },
+ .{ Error.UndefinedGroupOption, c.ONIGERR_UNDEFINED_GROUP_OPTION },
+ .{ Error.InvalidGroupOption, c.ONIGERR_INVALID_GROUP_OPTION },
+ .{ Error.InvalidPosixBracketType, c.ONIGERR_INVALID_POSIX_BRACKET_TYPE },
+ .{ Error.InvalidLookBehindPattern, c.ONIGERR_INVALID_LOOK_BEHIND_PATTERN },
+ .{ Error.InvalidRepeatRangePattern, c.ONIGERR_INVALID_REPEAT_RANGE_PATTERN },
+ .{ Error.TooBigNumber, c.ONIGERR_TOO_BIG_NUMBER },
+ .{ Error.TooBigNumberForRepeatRange, c.ONIGERR_TOO_BIG_NUMBER_FOR_REPEAT_RANGE },
+ .{ Error.UpperSmallerThanLowerInRepeatRange, c.ONIGERR_UPPER_SMALLER_THAN_LOWER_IN_REPEAT_RANGE },
+ .{ Error.EmptyRangeInCharClass, c.ONIGERR_EMPTY_RANGE_IN_CHAR_CLASS },
+ .{ Error.MismatchCodeLengthInClassRange, c.ONIGERR_MISMATCH_CODE_LENGTH_IN_CLASS_RANGE },
+ .{ Error.TooManyMultiByteRanges, c.ONIGERR_TOO_MANY_MULTI_BYTE_RANGES },
+ .{ Error.TooShortMultiByteString, c.ONIGERR_TOO_SHORT_MULTI_BYTE_STRING },
+ .{ Error.TooBigBackrefNumber, c.ONIGERR_TOO_BIG_BACKREF_NUMBER },
+ .{ Error.InvalidBackref, c.ONIGERR_INVALID_BACKREF },
+ .{ Error.NumberedBackrefOrCallNotAllowed, c.ONIGERR_NUMBERED_BACKREF_OR_CALL_NOT_ALLOWED },
+ .{ Error.TooManyCaptures, c.ONIGERR_TOO_MANY_CAPTURES },
+ .{ Error.TooLongWideCharValue, c.ONIGERR_TOO_LONG_WIDE_CHAR_VALUE },
+ .{ Error.UndefinedOperator, c.ONIGERR_UNDEFINED_OPERATOR },
+ .{ Error.EmptyGroupName, c.ONIGERR_EMPTY_GROUP_NAME },
+ .{ Error.InvalidGroupName, c.ONIGERR_INVALID_GROUP_NAME },
+ .{ Error.InvalidCharInGroupName, c.ONIGERR_INVALID_CHAR_IN_GROUP_NAME },
+ .{ Error.UndefinedNameReference, c.ONIGERR_UNDEFINED_NAME_REFERENCE },
+ .{ Error.UndefinedGroupReference, c.ONIGERR_UNDEFINED_GROUP_REFERENCE },
+ .{ Error.MultiplexDefinedName, c.ONIGERR_MULTIPLEX_DEFINED_NAME },
+ .{ Error.MultiplexDefinitionNameCall, c.ONIGERR_MULTIPLEX_DEFINITION_NAME_CALL },
+ .{ Error.NeverEndingRecursion, c.ONIGERR_NEVER_ENDING_RECURSION },
+ .{ Error.GroupNumberOverForCaptureHistory, c.ONIGERR_GROUP_NUMBER_OVER_FOR_CAPTURE_HISTORY },
+ .{ Error.InvalidCharPropertyName, c.ONIGERR_INVALID_CHAR_PROPERTY_NAME },
+ .{ Error.InvalidIfElseSyntax, c.ONIGERR_INVALID_IF_ELSE_SYNTAX },
+ .{ Error.InvalidAbsentGroupPattern, c.ONIGERR_INVALID_ABSENT_GROUP_PATTERN },
+ .{ Error.InvalidAbsentGroupGeneratorPattern, c.ONIGERR_INVALID_ABSENT_GROUP_GENERATOR_PATTERN },
+ .{ Error.InvalidCalloutPattern, c.ONIGERR_INVALID_CALLOUT_PATTERN },
+ .{ Error.InvalidCalloutName, c.ONIGERR_INVALID_CALLOUT_NAME },
+ .{ Error.UndefinedCalloutName, c.ONIGERR_UNDEFINED_CALLOUT_NAME },
+ .{ Error.InvalidCalloutBody, c.ONIGERR_INVALID_CALLOUT_BODY },
+ .{ Error.InvalidCalloutTagName, c.ONIGERR_INVALID_CALLOUT_TAG_NAME },
+ .{ Error.InvalidCalloutArg, c.ONIGERR_INVALID_CALLOUT_ARG },
+ .{ Error.InvalidCodePointValue, c.ONIGERR_INVALID_CODE_POINT_VALUE },
+ .{ Error.InvalidWideCharValue, c.ONIGERR_INVALID_WIDE_CHAR_VALUE },
+ .{ Error.TooBigWideCharValue, c.ONIGERR_TOO_BIG_WIDE_CHAR_VALUE },
+ .{ Error.NotSupportedEncodingCombination, c.ONIGERR_NOT_SUPPORTED_ENCODING_COMBINATION },
+ .{ Error.InvalidCombinationOfOptions, c.ONIGERR_INVALID_COMBINATION_OF_OPTIONS },
+ .{ Error.TooManyUserDefinedObjects, c.ONIGERR_TOO_MANY_USER_DEFINED_OBJECTS },
+ .{ Error.TooLongPropertyName, c.ONIGERR_TOO_LONG_PROPERTY_NAME },
+ .{ Error.VeryInefficientPattern, c.ONIGERR_VERY_INEFFICIENT_PATTERN },
+ .{ Error.LibraryIsNotInitialized, c.ONIGERR_LIBRARY_IS_NOT_INITIALIZED },
+};
diff --git a/vendor/libghostty-vt/pkg/oniguruma/init.zig b/vendor/libghostty-vt/pkg/oniguruma/init.zig
new file mode 100644
index 00000000..ea64724c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/init.zig
@@ -0,0 +1,16 @@
+const c = @import("c.zig").c;
+const Encoding = @import("types.zig").Encoding;
+const errors = @import("errors.zig");
+
+/// Call once per process to initialize Oniguruma. This should be given
+/// the encodings that the program will use.
+pub fn init(encs: []const *Encoding) !void {
+ _ = try errors.convertError(c.onig_initialize(
+ @ptrCast(@alignCast(@constCast(encs.ptr))),
+ @intCast(encs.len),
+ ));
+}
+
+pub fn deinit() void {
+ _ = c.onig_end();
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/main.zig b/vendor/libghostty-vt/pkg/oniguruma/main.zig
new file mode 100644
index 00000000..2541cc35
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/main.zig
@@ -0,0 +1,21 @@
+const initpkg = @import("init.zig");
+const match_param = @import("match_param.zig");
+const regex = @import("regex.zig");
+const region = @import("region.zig");
+const types = @import("types.zig");
+
+pub const c = @import("c.zig");
+pub const testing = @import("testing.zig");
+pub const errors = @import("errors.zig");
+
+pub const init = initpkg.init;
+pub const deinit = initpkg.deinit;
+pub const Encoding = types.Encoding;
+pub const MatchParam = match_param.MatchParam;
+pub const Regex = regex.Regex;
+pub const Region = region.Region;
+pub const Syntax = types.Syntax;
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/match_param.zig b/vendor/libghostty-vt/pkg/oniguruma/match_param.zig
new file mode 100644
index 00000000..b28258ff
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/match_param.zig
@@ -0,0 +1,23 @@
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const Error = errors.Error;
+
+pub const MatchParam = struct {
+ value: *c.OnigMatchParam,
+
+ pub fn init() !MatchParam {
+ const value = c.onig_new_match_param() orelse return Error.Memory;
+ return .{ .value = value };
+ }
+
+ pub fn deinit(self: *MatchParam) void {
+ c.onig_free_match_param(self.value);
+ }
+
+ pub fn setRetryLimitInSearch(self: *MatchParam, limit: usize) !void {
+ _ = try errors.convertError(c.onig_set_retry_limit_in_search_of_match_param(
+ self.value,
+ @intCast(limit),
+ ));
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/oniguruma/regex.zig b/vendor/libghostty-vt/pkg/oniguruma/regex.zig
new file mode 100644
index 00000000..fd920e01
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/regex.zig
@@ -0,0 +1,152 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const types = @import("types.zig");
+const errors = @import("errors.zig");
+const testEnsureInit = @import("testing.zig").ensureInit;
+const MatchParam = @import("match_param.zig").MatchParam;
+const Region = @import("region.zig").Region;
+const Error = errors.Error;
+const ErrorInfo = errors.ErrorInfo;
+const Encoding = types.Encoding;
+const Option = types.Option;
+const Syntax = types.Syntax;
+
+pub const Regex = struct {
+ value: c.OnigRegex,
+
+ pub fn init(
+ pattern: []const u8,
+ options: Option,
+ enc: *Encoding,
+ syntax: *Syntax,
+ err: ?*ErrorInfo,
+ ) !Regex {
+ var self: Regex = undefined;
+ _ = try errors.convertError(c.onig_new(
+ &self.value,
+ pattern.ptr,
+ pattern.ptr + pattern.len,
+ options.int(),
+ @ptrCast(@alignCast(enc)),
+ @ptrCast(@alignCast(syntax)),
+ @ptrCast(err),
+ ));
+ return self;
+ }
+
+ pub fn deinit(self: *Regex) void {
+ c.onig_free(self.value);
+ }
+
+ /// Search an entire string for matches. This always returns a region
+ /// which may heap allocate (C allocator).
+ pub fn search(
+ self: *Regex,
+ str: []const u8,
+ options: Option,
+ ) !Region {
+ return self.searchWithParam(str, options, null);
+ }
+
+ /// Search an entire string for matches. This always returns a region
+ /// which may heap allocate (C allocator).
+ pub fn searchWithParam(
+ self: *Regex,
+ str: []const u8,
+ options: Option,
+ match_param: ?*MatchParam,
+ ) !Region {
+ var region: Region = .{};
+
+ // As part of the searchAdvanced API call below, onig may allocate
+ // memory even if we fail to match. We need to deinit if there are
+ // any errors to free that memory.
+ errdefer region.deinit();
+
+ _ = try self.searchAdvancedWithParam(
+ str,
+ 0,
+ str.len,
+ &region,
+ options,
+ match_param,
+ );
+ return region;
+ }
+
+ /// onig_search directly
+ pub fn searchAdvanced(
+ self: *Regex,
+ str: []const u8,
+ start: usize,
+ end: usize,
+ region: *Region,
+ options: Option,
+ ) !usize {
+ return self.searchAdvancedWithParam(
+ str,
+ start,
+ end,
+ region,
+ options,
+ null,
+ );
+ }
+
+ /// onig_search_with_param directly
+ pub fn searchAdvancedWithParam(
+ self: *Regex,
+ str: []const u8,
+ start: usize,
+ end: usize,
+ region: *Region,
+ options: Option,
+ match_param: ?*MatchParam,
+ ) !usize {
+ const pos = try errors.convertError(if (match_param) |param|
+ c.onig_search_with_param(
+ self.value,
+ str.ptr,
+ str.ptr + str.len,
+ str.ptr + start,
+ str.ptr + end,
+ @ptrCast(region),
+ options.int(),
+ param.value,
+ )
+ else
+ c.onig_search(
+ self.value,
+ str.ptr,
+ str.ptr + str.len,
+ str.ptr + start,
+ str.ptr + end,
+ @ptrCast(region),
+ options.int(),
+ ));
+
+ return @intCast(pos);
+ }
+};
+
+test {
+ const testing = std.testing;
+
+ try testEnsureInit();
+ var re = try Regex.init("foo", .{}, Encoding.utf8, Syntax.default, null);
+ defer re.deinit();
+
+ var reg = try re.search("hello foo bar", .{});
+ defer reg.deinit();
+ try testing.expectEqual(@as(usize, 1), reg.count());
+
+ try testing.expectError(Error.Mismatch, re.search("hello", .{}));
+
+ var match_param = try MatchParam.init();
+ defer match_param.deinit();
+ try match_param.setRetryLimitInSearch(1000);
+
+ var reg_param = try re.searchWithParam("hello foo bar", .{}, &match_param);
+ defer reg_param.deinit();
+ try testing.expectEqual(@as(usize, 1), reg_param.count());
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/region.zig b/vendor/libghostty-vt/pkg/oniguruma/region.zig
new file mode 100644
index 00000000..8c5c7a52
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/region.zig
@@ -0,0 +1,51 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+pub const Region = extern struct {
+ allocated: c_int = 0,
+ num_regs: c_int = 0,
+ beg: ?[*]c_int = null,
+ end: ?[*]c_int = null,
+ history_root: ?*c.OnigCaptureTreeNode = null, // TODO: convert to Zig
+
+ pub fn deinit(self: *Region) void {
+ // We never free ourself because allocation of Region in the Zig
+ // bindings is handled by the Zig program.
+ c.onig_region_free(@ptrCast(self), 0);
+ }
+
+ /// Count the number of matches
+ pub fn count(self: *const Region) usize {
+ return @intCast(self.num_regs);
+ }
+
+ /// Iterate over the matched ranges.
+ pub fn iterator(self: *const Region) Iterator {
+ return .{ .region = self };
+ }
+
+ pub fn starts(self: *const Region) []const c_int {
+ if (self.num_regs == 0) return &.{};
+ return self.beg.?[0..@intCast(self.num_regs)];
+ }
+
+ pub fn ends(self: *const Region) []const c_int {
+ if (self.num_regs == 0) return &.{};
+ return self.end.?[0..@intCast(self.num_regs)];
+ }
+
+ pub const Iterator = struct {
+ region: *const Region,
+ i: usize = 0,
+
+ /// The next range
+ pub fn next(self: *Iterator) ?[2]usize {
+ if (self.i >= self.region.num_regs) return null;
+ defer self.i += 1;
+ return .{
+ @intCast(self.region.beg.?[self.i]),
+ @intCast(self.region.end.?[self.i]),
+ };
+ }
+ };
+};
diff --git a/vendor/libghostty-vt/pkg/oniguruma/testing.zig b/vendor/libghostty-vt/pkg/oniguruma/testing.zig
new file mode 100644
index 00000000..234b5e75
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/testing.zig
@@ -0,0 +1,15 @@
+const init = @import("init.zig");
+const Encoding = @import("types.zig").Encoding;
+
+var initialized: bool = false;
+
+/// Call this function before any other tests in this package to ensure that
+/// the oni library is initialized. This should only be used for tests
+/// and only when you're sure this is the ONLY way that oni is being
+/// initialized.
+///
+/// This always only initializes the encodings the tests use.
+pub fn ensureInit() !void {
+ if (initialized) return;
+ try init.init(&.{Encoding.utf8});
+}
diff --git a/vendor/libghostty-vt/pkg/oniguruma/types.zig b/vendor/libghostty-vt/pkg/oniguruma/types.zig
new file mode 100644
index 00000000..84a0a7c9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/oniguruma/types.zig
@@ -0,0 +1,96 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+pub const Encoding = opaque {
+ pub const ascii: *Encoding = @ptrCast(c.ONIG_ENCODING_ASCII());
+ pub const iso_8859_1: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_1());
+ pub const iso_8859_2: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_2());
+ pub const iso_8859_3: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_3());
+ pub const iso_8859_4: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_4());
+ pub const iso_8859_5: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_5());
+ pub const iso_8859_6: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_6());
+ pub const iso_8859_7: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_7());
+ pub const iso_8859_8: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_8());
+ pub const iso_8859_9: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_9());
+ pub const iso_8859_10: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_10());
+ pub const iso_8859_11: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_11());
+ pub const iso_8859_13: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_13());
+ pub const iso_8859_14: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_14());
+ pub const iso_8859_15: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_15());
+ pub const iso_8859_16: *Encoding = @ptrCast(c.ONIG_ENCODING_ISO_8859_16());
+ pub const utf8: *Encoding = @ptrCast(c.ONIG_ENCODING_UTF8());
+ pub const utf16_be: *Encoding = @ptrCast(c.ONIG_ENCODING_UTF16_BE());
+ pub const utf16_le: *Encoding = @ptrCast(c.ONIG_ENCODING_UTF16_LE());
+ pub const utf32_be: *Encoding = @ptrCast(c.ONIG_ENCODING_UTF32_BE());
+ pub const utf32_le: *Encoding = @ptrCast(c.ONIG_ENCODING_UTF32_LE());
+ pub const euc_jp: *Encoding = @ptrCast(c.ONIG_ENCODING_EUC_JP());
+ pub const euc_tw: *Encoding = @ptrCast(c.ONIG_ENCODING_EUC_TW());
+ pub const euc_kr: *Encoding = @ptrCast(c.ONIG_ENCODING_EUC_KR());
+ pub const euc_cn: *Encoding = @ptrCast(c.ONIG_ENCODING_EUC_CN());
+ pub const sjis: *Encoding = @ptrCast(c.ONIG_ENCODING_SJIS());
+ pub const koi8: *Encoding = @ptrCast(c.ONIG_ENCODING_KOI8());
+ pub const koi8_r: *Encoding = @ptrCast(c.ONIG_ENCODING_KOI8_R());
+ pub const cp1251: *Encoding = @ptrCast(c.ONIG_ENCODING_CP1251());
+ pub const big5: *Encoding = @ptrCast(c.ONIG_ENCODING_BIG5());
+ pub const gb18030: *Encoding = @ptrCast(c.ONIG_ENCODING_GB18030());
+};
+
+pub const Syntax = opaque {
+ pub const default: *Syntax = @ptrCast(c.ONIG_SYNTAX_ONIGURUMA());
+ pub const asis: *Syntax = @ptrCast(c.ONIG_SYNTAX_ASIS());
+ pub const posix_basic: *Syntax = @ptrCast(c.ONIG_SYNTAX_POSIX_BASIC());
+ pub const posix_extended: *Syntax = @ptrCast(c.ONIG_SYNTAX_POSIX_EXTENDED());
+ pub const emacs: *Syntax = @ptrCast(c.ONIG_SYNTAX_EMACS());
+ pub const grep: *Syntax = @ptrCast(c.ONIG_SYNTAX_GREP());
+ pub const gnu_regex: *Syntax = @ptrCast(c.ONIG_SYNTAX_GNU_REGEX());
+ pub const java: *Syntax = @ptrCast(c.ONIG_SYNTAX_JAVA());
+ pub const perl: *Syntax = @ptrCast(c.ONIG_SYNTAX_PERL());
+ pub const perl_ng: *Syntax = @ptrCast(c.ONIG_SYNTAX_PERL_NG());
+ pub const ruby: *Syntax = @ptrCast(c.ONIG_SYNTAX_RUBY());
+ pub const oniguruma: *Syntax = @ptrCast(c.ONIG_SYNTAX_ONIGURUMA());
+};
+
+pub const Option = packed struct(c_uint) {
+ ignorecase: bool = false,
+ extend: bool = false,
+ multiline: bool = false,
+ singleline: bool = false,
+ find_longest: bool = false,
+ find_not_empty: bool = false,
+ negate_singleline: bool = false,
+ dont_capture_group: bool = false,
+ capture_group: bool = false,
+ // search time
+ notbol: bool = false,
+ noteol: bool = false,
+ posix_region: bool = false,
+ check_validity_of_string: bool = false,
+ // compile time
+ ignorecase_is_ascii: bool = false,
+ word_is_ascii: bool = false,
+ digit_is_ascii: bool = false,
+ space_is_ascii: bool = false,
+ posix_is_ascii: bool = false,
+ text_segment_extended_grapheme_cluster: bool = false,
+ text_segment_word: bool = false,
+ // search time
+ not_begin_string: bool = false,
+ not_end_string: bool = false,
+ not_begin_position: bool = false,
+ callback_each_match: bool = false,
+ match_whole_string: bool = false,
+
+ _padding: u7 = 0,
+
+ pub fn int(self: Option) c_uint {
+ return @bitCast(self);
+ }
+
+ test "order" {
+ const testing = std.testing;
+ const opt: Option = .{ .extend = true };
+ try testing.expectEqual(c.ONIG_OPTION_EXTEND, opt.int());
+ }
+};
+
+test {}
diff --git a/vendor/libghostty-vt/pkg/opengl/Buffer.zig b/vendor/libghostty-vt/pkg/opengl/Buffer.zig
new file mode 100644
index 00000000..60934295
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Buffer.zig
@@ -0,0 +1,245 @@
+const Buffer = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+id: c.GLuint,
+
+/// Create a single buffer.
+pub fn create() !Buffer {
+ var vbo: c.GLuint = undefined;
+ glad.context.GenBuffers.?(1, &vbo);
+ return Buffer{ .id = vbo };
+}
+
+/// glBindBuffer
+pub fn bind(self: Buffer, target: Target) !Binding {
+ glad.context.BindBuffer.?(@intFromEnum(target), self.id);
+ return Binding{ .id = self.id, .target = target };
+}
+
+pub fn destroy(self: Buffer) void {
+ glad.context.DeleteBuffers.?(1, &self.id);
+}
+
+pub fn bindBase(self: Buffer, target: Target, idx: c.GLuint) !void {
+ glad.context.BindBufferBase.?(
+ @intFromEnum(target),
+ idx,
+ self.id,
+ );
+ try errors.getError();
+}
+
+/// Binding is a bound buffer. By using this for functions that operate
+/// on bound buffers, you can easily defer unbinding and in safety-enabled
+/// modes verify that unbound buffers are never accessed.
+pub const Binding = struct {
+ id: c.GLuint,
+ target: Target,
+
+ pub fn unbind(b: Binding) void {
+ glad.context.BindBuffer.?(@intFromEnum(b.target), 0);
+ }
+
+ /// Sets the data of this bound buffer. The data can be any array-like
+ /// type. The size of the data is automatically determined based on the type.
+ pub fn setData(
+ b: Binding,
+ data: anytype,
+ usage: Usage,
+ ) !void {
+ const info = dataInfo(data);
+ glad.context.BufferData.?(
+ @intFromEnum(b.target),
+ info.size,
+ info.ptr,
+ @intFromEnum(usage),
+ );
+ try errors.getError();
+ }
+
+ /// Sets the data of this bound buffer. The data can be any array-like
+ /// type. The size of the data is automatically determined based on the type.
+ pub fn setSubData(
+ b: Binding,
+ offset: usize,
+ data: anytype,
+ ) !void {
+ const info = dataInfo(data);
+ glad.context.BufferSubData.?(
+ @intFromEnum(b.target),
+ @intCast(offset),
+ info.size,
+ info.ptr,
+ );
+ try errors.getError();
+ }
+
+ /// Sets the buffer data with a null buffer that is expected to be
+ /// filled in the future using subData. This requires the type just so
+ /// we can setup the data size.
+ pub fn setDataNull(
+ b: Binding,
+ comptime T: type,
+ usage: Usage,
+ ) !void {
+ glad.context.BufferData.?(
+ @intFromEnum(b.target),
+ @sizeOf(T),
+ null,
+ @intFromEnum(usage),
+ );
+ try errors.getError();
+ }
+
+ /// Same as setDataNull but lets you manually specify the buffer size.
+ pub fn setDataNullManual(
+ b: Binding,
+ size: usize,
+ usage: Usage,
+ ) !void {
+ glad.context.BufferData.?(
+ @intFromEnum(b.target),
+ @intCast(size),
+ null,
+ @intFromEnum(usage),
+ );
+ try errors.getError();
+ }
+
+ fn dataInfo(data: anytype) struct {
+ size: isize,
+ ptr: *const anyopaque,
+ } {
+ return switch (@typeInfo(@TypeOf(data))) {
+ .pointer => |ptr| switch (ptr.size) {
+ .one => .{
+ .size = @sizeOf(ptr.child),
+ .ptr = data,
+ },
+ .slice => .{
+ .size = @intCast(@sizeOf(ptr.child) * data.len),
+ .ptr = data.ptr,
+ },
+ else => {
+ std.log.err("invalid buffer data pointer size: {}", .{ptr.size});
+ unreachable;
+ },
+ },
+ else => {
+ std.log.err("invalid buffer data type: {s}", .{@tagName(@typeInfo(@TypeOf(data)))});
+ unreachable;
+ },
+ };
+ }
+
+ /// Shorthand for vertexAttribPointer that is specialized towards the
+ /// common use case of specifying an array of homogeneous types that
+ /// don't need normalization. This also enables the attribute at idx.
+ pub fn attribute(
+ b: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ comptime T: type,
+ offset: usize,
+ ) !void {
+ const info: struct {
+ // Type of the each component in the array.
+ typ: c.GLenum,
+
+ // The byte offset between each full set of attributes.
+ stride: c.GLsizei,
+
+ // The size of each component used in calculating the offset.
+ offset: usize,
+ } = switch (@typeInfo(T)) {
+ .Array => |ary| .{
+ .typ = switch (ary.child) {
+ f32 => c.GL_FLOAT,
+ else => @compileError("unsupported array child type"),
+ },
+ .offset = @sizeOf(ary.child),
+ .stride = @sizeOf(T),
+ },
+ else => @compileError("unsupported type"),
+ };
+
+ try b.attributeAdvanced(
+ idx,
+ size,
+ info.typ,
+ false,
+ info.stride,
+ offset * info.offset,
+ );
+ try b.enableAttribArray(idx);
+ }
+
+ /// VertexAttribDivisor
+ pub fn attributeDivisor(_: Binding, idx: c.GLuint, divisor: c.GLuint) !void {
+ glad.context.VertexAttribDivisor.?(idx, divisor);
+ try errors.getError();
+ }
+
+ pub fn attributeAdvanced(
+ _: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ typ: c.GLenum,
+ normalized: bool,
+ stride: c.GLsizei,
+ offset: usize,
+ ) !void {
+ const normalized_c: c.GLboolean = if (normalized) c.GL_TRUE else c.GL_FALSE;
+ const offsetPtr = if (offset > 0)
+ @as(*const anyopaque, @ptrFromInt(offset))
+ else
+ null;
+
+ glad.context.VertexAttribPointer.?(idx, size, typ, normalized_c, stride, offsetPtr);
+ try errors.getError();
+ }
+
+ pub fn attributeIAdvanced(
+ _: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ typ: c.GLenum,
+ stride: c.GLsizei,
+ offset: usize,
+ ) !void {
+ const offsetPtr = if (offset > 0)
+ @as(*const anyopaque, @ptrFromInt(offset))
+ else
+ null;
+
+ glad.context.VertexAttribIPointer.?(idx, size, typ, stride, offsetPtr);
+ try errors.getError();
+ }
+};
+
+/// Enum for possible binding targets.
+pub const Target = enum(c_uint) {
+ array = c.GL_ARRAY_BUFFER,
+ element_array = c.GL_ELEMENT_ARRAY_BUFFER,
+ uniform = c.GL_UNIFORM_BUFFER,
+ storage = c.GL_SHADER_STORAGE_BUFFER,
+ _,
+};
+
+/// Enum for possible buffer usages.
+pub const Usage = enum(c_uint) {
+ stream_draw = c.GL_STREAM_DRAW,
+ stream_read = c.GL_STREAM_READ,
+ stream_copy = c.GL_STREAM_COPY,
+ static_draw = c.GL_STATIC_DRAW,
+ static_read = c.GL_STATIC_READ,
+ static_copy = c.GL_STATIC_COPY,
+ dynamic_draw = c.GL_DYNAMIC_DRAW,
+ dynamic_read = c.GL_DYNAMIC_READ,
+ dynamic_copy = c.GL_DYNAMIC_COPY,
+ _,
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/Framebuffer.zig b/vendor/libghostty-vt/pkg/opengl/Framebuffer.zig
new file mode 100644
index 00000000..ea1f0d2b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Framebuffer.zig
@@ -0,0 +1,116 @@
+const Framebuffer = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+const Texture = @import("Texture.zig");
+const Renderbuffer = @import("Renderbuffer.zig");
+
+id: c.GLuint,
+
+/// Create a single buffer.
+pub fn create() !Framebuffer {
+ var fbo: c.GLuint = undefined;
+ glad.context.GenFramebuffers.?(1, &fbo);
+ return .{ .id = fbo };
+}
+
+pub fn destroy(v: Framebuffer) void {
+ glad.context.DeleteFramebuffers.?(1, &v.id);
+}
+
+pub fn bind(v: Framebuffer, target: Target) !Binding {
+ // The default framebuffer is documented as being zero but
+ // on multiple OpenGL drivers its not zero, so we grab it
+ // at runtime.
+ var current: c.GLint = undefined;
+ glad.context.GetIntegerv.?(c.GL_FRAMEBUFFER_BINDING, &current);
+ glad.context.BindFramebuffer.?(@intFromEnum(target), v.id);
+ return .{ .target = target, .previous = @intCast(current) };
+}
+
+/// Enum for possible binding targets.
+pub const Target = enum(c_uint) {
+ framebuffer = c.GL_FRAMEBUFFER,
+ draw = c.GL_DRAW_FRAMEBUFFER,
+ read = c.GL_READ_FRAMEBUFFER,
+ _,
+};
+
+pub const Attachment = enum(c_uint) {
+ color0 = c.GL_COLOR_ATTACHMENT0,
+ depth = c.GL_DEPTH_ATTACHMENT,
+ stencil = c.GL_STENCIL_ATTACHMENT,
+ depth_stencil = c.GL_DEPTH_STENCIL_ATTACHMENT,
+ _,
+};
+
+pub const Status = enum(c_uint) {
+ complete = c.GL_FRAMEBUFFER_COMPLETE,
+ undefined = c.GL_FRAMEBUFFER_UNDEFINED,
+ incomplete_attachment = c.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT,
+ incomplete_missing_attachment = c.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT,
+ incomplete_draw_buffer = c.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER,
+ incomplete_read_buffer = c.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER,
+ unsupported = c.GL_FRAMEBUFFER_UNSUPPORTED,
+ incomplete_multisample = c.GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE,
+ incomplete_layer_targets = c.GL_FRAMEBUFFER_INCOMPLETE_LAYER_TARGETS,
+ _,
+};
+
+pub const Binding = struct {
+ target: Target,
+ previous: c.GLuint,
+
+ pub fn unbind(self: Binding) void {
+ glad.context.BindFramebuffer.?(
+ @intFromEnum(self.target),
+ self.previous,
+ );
+ }
+
+ pub fn texture2D(
+ self: Binding,
+ attachment: Attachment,
+ textarget: Texture.Target,
+ texture: Texture,
+ level: c.GLint,
+ ) !void {
+ glad.context.FramebufferTexture2D.?(
+ @intFromEnum(self.target),
+ @intFromEnum(attachment),
+ @intFromEnum(textarget),
+ texture.id,
+ level,
+ );
+ try errors.getError();
+ }
+
+ pub fn renderbuffer(
+ self: Binding,
+ attachment: Attachment,
+ buffer: Renderbuffer,
+ ) !void {
+ glad.context.FramebufferRenderbuffer.?(
+ @intFromEnum(self.target),
+ @intFromEnum(attachment),
+ c.GL_RENDERBUFFER,
+ buffer.id,
+ );
+ try errors.getError();
+ }
+
+ pub fn drawBuffers(
+ self: Binding,
+ bufs: []Attachment,
+ ) !void {
+ _ = self;
+ glad.context.DrawBuffers.?(@intCast(bufs.len), bufs.ptr);
+ try errors.getError();
+ }
+
+ pub fn checkStatus(self: Binding) Status {
+ return @enumFromInt(glad.context.CheckFramebufferStatus.?(@intFromEnum(self.target)));
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/Program.zig b/vendor/libghostty-vt/pkg/opengl/Program.zig
new file mode 100644
index 00000000..ee77582c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Program.zig
@@ -0,0 +1,137 @@
+const Program = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const log = std.log.scoped(.opengl);
+
+const c = @import("c.zig").c;
+const Shader = @import("Shader.zig");
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+id: c.GLuint,
+
+pub const Binding = struct {
+ pub fn unbind(_: Binding) void {
+ glad.context.UseProgram.?(0);
+ }
+};
+
+pub fn create() !Program {
+ const id = glad.context.CreateProgram.?();
+ if (id == 0) try errors.mustError();
+ log.debug("program created id={}", .{id});
+ return .{ .id = id };
+}
+
+/// Create a program from a vertex and fragment shader source. This will
+/// compile and link the vertex and fragment shader.
+pub fn createVF(vsrc: [:0]const u8, fsrc: [:0]const u8) !Program {
+ const vs = try Shader.create(c.GL_VERTEX_SHADER);
+ try vs.setSourceAndCompile(vsrc);
+ defer vs.destroy();
+
+ const fs = try Shader.create(c.GL_FRAGMENT_SHADER);
+ try fs.setSourceAndCompile(fsrc);
+ defer fs.destroy();
+
+ const p = try create();
+ try p.attachShader(vs);
+ try p.attachShader(fs);
+ try p.link();
+
+ return p;
+}
+
+pub fn destroy(p: Program) void {
+ assert(p.id != 0);
+ glad.context.DeleteProgram.?(p.id);
+ log.debug("program destroyed id={}", .{p.id});
+}
+
+pub fn attachShader(p: Program, s: Shader) !void {
+ glad.context.AttachShader.?(p.id, s.id);
+ try errors.getError();
+}
+
+pub fn link(p: Program) !void {
+ glad.context.LinkProgram.?(p.id);
+
+ // Check if linking succeeded
+ var success: c_int = undefined;
+ glad.context.GetProgramiv.?(p.id, c.GL_LINK_STATUS, &success);
+ if (success == c.GL_TRUE) {
+ log.debug("program linked id={}", .{p.id});
+ return;
+ }
+
+ log.err("program link failure id={} message={s}", .{
+ p.id,
+ std.mem.sliceTo(&p.getInfoLog(), 0),
+ });
+ return error.CompileFailed;
+}
+
+pub fn use(p: Program) !Binding {
+ glad.context.UseProgram.?(p.id);
+ try errors.getError();
+ return .{};
+}
+
+pub fn uniformBlockBinding(
+ self: Program,
+ index: c.GLuint,
+ binding: c.GLuint,
+) !void {
+ glad.context.UniformBlockBinding.?(self.id, index, binding);
+ try errors.getError();
+}
+
+/// Requires the program is currently in use.
+pub fn setUniform(
+ p: Program,
+ n: [:0]const u8,
+ value: anytype,
+) !void {
+ const loc = glad.context.GetUniformLocation.?(
+ p.id,
+ @ptrCast(n.ptr),
+ );
+ if (loc < 0) {
+ return error.UniformNameInvalid;
+ }
+ try errors.getError();
+
+ // Perform the correct call depending on the type of the value.
+ switch (@TypeOf(value)) {
+ bool => glad.context.Uniform1i.?(loc, if (value) 1 else 0),
+ comptime_int => glad.context.Uniform1i.?(loc, value),
+ f32 => glad.context.Uniform1f.?(loc, value),
+ @Vector(2, f32) => glad.context.Uniform2f.?(loc, value[0], value[1]),
+ @Vector(3, f32) => glad.context.Uniform3f.?(loc, value[0], value[1], value[2]),
+ @Vector(4, f32) => glad.context.Uniform4f.?(loc, value[0], value[1], value[2], value[3]),
+ [4]@Vector(4, f32) => glad.context.UniformMatrix4fv.?(
+ loc,
+ 1,
+ c.GL_FALSE,
+ @ptrCast(&value),
+ ),
+ else => {
+ log.warn("unsupported uniform type {}", .{@TypeOf(value)});
+ unreachable;
+ },
+ }
+ try errors.getError();
+}
+
+/// getInfoLog returns the info log for this program. This attempts to
+/// keep the log fully stack allocated and is therefore limited to a max
+/// amount of elements.
+//
+// NOTE(mitchellh): we can add a dynamic version that uses an allocator
+// if we ever need it.
+pub fn getInfoLog(s: Program) [512]u8 {
+ var msg: [512]u8 = undefined;
+ glad.context.GetProgramInfoLog.?(s.id, msg.len, null, &msg);
+ return msg;
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/Renderbuffer.zig b/vendor/libghostty-vt/pkg/opengl/Renderbuffer.zig
new file mode 100644
index 00000000..ef21287f
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Renderbuffer.zig
@@ -0,0 +1,56 @@
+const Renderbuffer = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+const Texture = @import("Texture.zig");
+
+id: c.GLuint,
+
+/// Create a single buffer.
+pub fn create() !Renderbuffer {
+ var rbo: c.GLuint = undefined;
+ glad.context.GenRenderbuffers.?(1, &rbo);
+ return .{ .id = rbo };
+}
+
+pub fn destroy(v: Renderbuffer) void {
+ glad.context.DeleteRenderbuffers.?(1, &v.id);
+}
+
+pub fn bind(v: Renderbuffer) !Binding {
+ // Keep track of the previous binding so we can restore it in unbind.
+ var current: c.GLint = undefined;
+ glad.context.GetIntegerv.?(c.GL_RENDERBUFFER_BINDING, &current);
+ glad.context.BindRenderbuffer.?(c.GL_RENDERBUFFER, v.id);
+ return .{ .previous = @intCast(current) };
+}
+
+pub const Binding = struct {
+ previous: c.GLuint,
+
+ pub fn unbind(self: Binding) void {
+ glad.context.BindRenderbuffer.?(
+ c.GL_RENDERBUFFER,
+ self.previous,
+ );
+ }
+
+ pub fn storage(
+ self: Binding,
+ format: Texture.InternalFormat,
+ width: c.GLsizei,
+ height: c.GLsizei,
+ ) !void {
+ _ = self;
+ glad.context.RenderbufferStorage.?(
+ c.GL_RENDERBUFFER,
+ @intCast(@intFromEnum(format)),
+ width,
+ height,
+ );
+ try errors.getError();
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/Sampler.zig b/vendor/libghostty-vt/pkg/opengl/Sampler.zig
new file mode 100644
index 00000000..f5c15699
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Sampler.zig
@@ -0,0 +1,43 @@
+const Sampler = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+const Texture = @import("Texture.zig");
+
+id: c.GLuint,
+
+/// Create a single sampler.
+pub fn create() errors.Error!Sampler {
+ var id: c.GLuint = undefined;
+ glad.context.GenSamplers.?(1, &id);
+ try errors.getError();
+ return .{ .id = id };
+}
+
+/// glBindSampler
+pub fn bind(v: Sampler, index: c_uint) !void {
+ glad.context.BindSampler.?(index, v.id);
+ try errors.getError();
+}
+
+pub fn parameter(
+ self: Sampler,
+ name: Texture.Parameter,
+ value: anytype,
+) errors.Error!void {
+ switch (@TypeOf(value)) {
+ c.GLint => glad.context.SamplerParameteri.?(
+ self.id,
+ @intFromEnum(name),
+ value,
+ ),
+ else => unreachable,
+ }
+ try errors.getError();
+}
+
+pub fn destroy(v: Sampler) void {
+ glad.context.DeleteSamplers.?(1, &v.id);
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/Shader.zig b/vendor/libghostty-vt/pkg/opengl/Shader.zig
new file mode 100644
index 00000000..8973ace3
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Shader.zig
@@ -0,0 +1,56 @@
+const Shader = @This();
+
+const std = @import("std");
+const assert = std.debug.assert;
+const log = std.log.scoped(.opengl);
+
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+id: c.GLuint,
+
+pub inline fn create(typ: c.GLenum) errors.Error!Shader {
+ const id = glad.context.CreateShader.?(typ);
+ if (id == 0) {
+ try errors.mustError();
+ unreachable;
+ }
+
+ log.debug("shader created id={}", .{id});
+ return Shader{ .id = id };
+}
+
+/// Set the source and compile a shader.
+pub inline fn setSourceAndCompile(s: Shader, source: [:0]const u8) !void {
+ glad.context.ShaderSource.?(s.id, 1, &@as([*c]const u8, @ptrCast(source)), null);
+ glad.context.CompileShader.?(s.id);
+
+ // Check if compilation succeeded
+ var success: c_int = undefined;
+ glad.context.GetShaderiv.?(s.id, c.GL_COMPILE_STATUS, &success);
+ if (success == c.GL_TRUE) return;
+ log.err("shader compilation failure id={} message={s}", .{
+ s.id,
+ std.mem.sliceTo(&s.getInfoLog(), 0),
+ });
+ return error.CompileFailed;
+}
+
+/// getInfoLog returns the info log for this shader. This attempts to
+/// keep the log fully stack allocated and is therefore limited to a max
+/// amount of elements.
+//
+// NOTE(mitchellh): we can add a dynamic version that uses an allocator
+// if we ever need it.
+pub inline fn getInfoLog(s: Shader) [512]u8 {
+ var msg: [512]u8 = undefined;
+ glad.context.GetShaderInfoLog.?(s.id, msg.len, null, &msg);
+ return msg;
+}
+
+pub inline fn destroy(s: Shader) void {
+ assert(s.id != 0);
+ glad.context.DeleteShader.?(s.id);
+ log.debug("shader destroyed id={}", .{s.id});
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/Texture.zig b/vendor/libghostty-vt/pkg/opengl/Texture.zig
new file mode 100644
index 00000000..03e79485
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/Texture.zig
@@ -0,0 +1,221 @@
+const Texture = @This();
+
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+id: c.GLuint,
+
+pub fn active(index: c_uint) errors.Error!void {
+ glad.context.ActiveTexture.?(index + c.GL_TEXTURE0);
+ try errors.getError();
+}
+
+/// Create a single texture.
+pub fn create() errors.Error!Texture {
+ var id: c.GLuint = undefined;
+ glad.context.GenTextures.?(1, &id);
+ try errors.getError();
+ return .{ .id = id };
+}
+
+/// glBindTexture
+pub fn bind(v: Texture, target: Target) !Binding {
+ glad.context.BindTexture.?(@intFromEnum(target), v.id);
+ try errors.getError();
+ return .{ .target = target };
+}
+
+pub fn destroy(v: Texture) void {
+ glad.context.DeleteTextures.?(1, &v.id);
+}
+
+/// Enum for possible texture binding targets.
+pub const Target = enum(c_uint) {
+ @"1D" = c.GL_TEXTURE_1D,
+ @"2D" = c.GL_TEXTURE_2D,
+ @"3D" = c.GL_TEXTURE_3D,
+ @"1DArray" = c.GL_TEXTURE_1D_ARRAY,
+ @"2DArray" = c.GL_TEXTURE_2D_ARRAY,
+ Rectangle = c.GL_TEXTURE_RECTANGLE,
+ CubeMap = c.GL_TEXTURE_CUBE_MAP,
+ Buffer = c.GL_TEXTURE_BUFFER,
+ @"2DMultisample" = c.GL_TEXTURE_2D_MULTISAMPLE,
+ @"2DMultisampleArray" = c.GL_TEXTURE_2D_MULTISAMPLE_ARRAY,
+};
+
+/// Enum for possible texture parameters.
+pub const Parameter = enum(c_uint) {
+ BaseLevel = c.GL_TEXTURE_BASE_LEVEL,
+ CompareFunc = c.GL_TEXTURE_COMPARE_FUNC,
+ CompareMode = c.GL_TEXTURE_COMPARE_MODE,
+ LodBias = c.GL_TEXTURE_LOD_BIAS,
+ MinFilter = c.GL_TEXTURE_MIN_FILTER,
+ MagFilter = c.GL_TEXTURE_MAG_FILTER,
+ MinLod = c.GL_TEXTURE_MIN_LOD,
+ MaxLod = c.GL_TEXTURE_MAX_LOD,
+ MaxLevel = c.GL_TEXTURE_MAX_LEVEL,
+ SwizzleR = c.GL_TEXTURE_SWIZZLE_R,
+ SwizzleG = c.GL_TEXTURE_SWIZZLE_G,
+ SwizzleB = c.GL_TEXTURE_SWIZZLE_B,
+ SwizzleA = c.GL_TEXTURE_SWIZZLE_A,
+ WrapS = c.GL_TEXTURE_WRAP_S,
+ WrapT = c.GL_TEXTURE_WRAP_T,
+ WrapR = c.GL_TEXTURE_WRAP_R,
+};
+
+/// Internal format enum for texture images.
+pub const InternalFormat = enum(c_int) {
+ red = c.GL_RED,
+ rgb = c.GL_RGB8,
+ rgba = c.GL_RGBA8,
+
+ srgb = c.GL_SRGB8,
+ srgba = c.GL_SRGB8_ALPHA8,
+
+ rgba_compressed = c.GL_COMPRESSED_RGBA_BPTC_UNORM,
+ srgba_compressed = c.GL_COMPRESSED_SRGB_ALPHA_BPTC_UNORM,
+
+ // There are so many more that I haven't filled in.
+ _,
+};
+
+/// Format for texture images
+pub const Format = enum(c_uint) {
+ red = c.GL_RED,
+ rgb = c.GL_RGB,
+ rgba = c.GL_RGBA,
+ bgra = c.GL_BGRA,
+
+ // There are so many more that I haven't filled in.
+ _,
+};
+
+/// Minification filter for textures.
+pub const MinFilter = enum(c_int) {
+ nearest = c.GL_NEAREST,
+ linear = c.GL_LINEAR,
+ nearest_mipmap_nearest = c.GL_NEAREST_MIPMAP_NEAREST,
+ linear_mipmap_nearest = c.GL_LINEAR_MIPMAP_NEAREST,
+ nearest_mipmap_linear = c.GL_NEAREST_MIPMAP_LINEAR,
+ linear_mipmap_linear = c.GL_LINEAR_MIPMAP_LINEAR,
+};
+
+/// Magnification filter for textures.
+pub const MagFilter = enum(c_int) {
+ nearest = c.GL_NEAREST,
+ linear = c.GL_LINEAR,
+};
+
+/// Texture coordinate wrapping mode.
+pub const Wrap = enum(c_int) {
+ clamp_to_edge = c.GL_CLAMP_TO_EDGE,
+ clamp_to_border = c.GL_CLAMP_TO_BORDER,
+ mirrored_repeat = c.GL_MIRRORED_REPEAT,
+ repeat = c.GL_REPEAT,
+};
+
+/// Data type for texture images.
+pub const DataType = enum(c_uint) {
+ UnsignedByte = c.GL_UNSIGNED_BYTE,
+
+ // There are so many more that I haven't filled in.
+ _,
+};
+
+pub const Binding = struct {
+ target: Target,
+
+ pub fn unbind(b: *const Binding) void {
+ glad.context.BindTexture.?(@intFromEnum(b.target), 0);
+ }
+
+ pub fn generateMipmap(b: Binding) void {
+ glad.context.GenerateMipmap.?(@intFromEnum(b.target));
+ }
+
+ pub fn parameter(b: Binding, name: Parameter, value: anytype) errors.Error!void {
+ switch (@TypeOf(value)) {
+ c.GLint => glad.context.TexParameteri.?(
+ @intFromEnum(b.target),
+ @intFromEnum(name),
+ value,
+ ),
+ else => unreachable,
+ }
+ try errors.getError();
+ }
+
+ pub fn image2D(
+ b: Binding,
+ level: c.GLint,
+ internal_format: InternalFormat,
+ width: c.GLsizei,
+ height: c.GLsizei,
+ format: Format,
+ typ: DataType,
+ data: ?*const anyopaque,
+ ) errors.Error!void {
+ glad.context.TexImage2D.?(
+ @intFromEnum(b.target),
+ level,
+ @intFromEnum(internal_format),
+ width,
+ height,
+ 0,
+ @intFromEnum(format),
+ @intFromEnum(typ),
+ data,
+ );
+ try errors.getError();
+ }
+
+ pub fn subImage2D(
+ b: Binding,
+ level: c.GLint,
+ xoffset: c.GLint,
+ yoffset: c.GLint,
+ width: c.GLsizei,
+ height: c.GLsizei,
+ format: Format,
+ typ: DataType,
+ data: ?*const anyopaque,
+ ) errors.Error!void {
+ glad.context.TexSubImage2D.?(
+ @intFromEnum(b.target),
+ level,
+ xoffset,
+ yoffset,
+ width,
+ height,
+ @intFromEnum(format),
+ @intFromEnum(typ),
+ data,
+ );
+ try errors.getError();
+ }
+
+ pub fn copySubImage2D(
+ b: Binding,
+ level: c.GLint,
+ xoffset: c.GLint,
+ yoffset: c.GLint,
+ x: c.GLint,
+ y: c.GLint,
+ width: c.GLsizei,
+ height: c.GLsizei,
+ ) errors.Error!void {
+ glad.context.CopyTexSubImage2D.?(
+ @intFromEnum(b.target),
+ level,
+ xoffset,
+ yoffset,
+ x,
+ y,
+ width,
+ height,
+ );
+ try errors.getError();
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/VertexArray.zig b/vendor/libghostty-vt/pkg/opengl/VertexArray.zig
new file mode 100644
index 00000000..44bf3162
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/VertexArray.zig
@@ -0,0 +1,116 @@
+const VertexArray = @This();
+
+const c = @import("c.zig").c;
+const glad = @import("glad.zig");
+const errors = @import("errors.zig");
+
+id: c.GLuint,
+
+/// Create a single vertex array object.
+pub fn create() !VertexArray {
+ var vao: c.GLuint = undefined;
+ glad.context.GenVertexArrays.?(1, &vao);
+ return VertexArray{ .id = vao };
+}
+
+/// glBindVertexArray
+pub fn bind(v: VertexArray) !Binding {
+ glad.context.BindVertexArray.?(v.id);
+ try errors.getError();
+ return .{};
+}
+
+pub fn destroy(v: VertexArray) void {
+ glad.context.DeleteVertexArrays.?(1, &v.id);
+}
+
+pub const Binding = struct {
+ pub fn unbind(self: Binding) void {
+ _ = self;
+ glad.context.BindVertexArray.?(0);
+ }
+
+ pub fn enableAttribArray(_: Binding, idx: c.GLuint) !void {
+ glad.context.EnableVertexAttribArray.?(idx);
+ try errors.getError();
+ }
+
+ pub fn bindingDivisor(_: Binding, idx: c.GLuint, divisor: c.GLuint) !void {
+ glad.context.VertexBindingDivisor.?(idx, divisor);
+ try errors.getError();
+ }
+
+ pub fn attributeBinding(
+ _: Binding,
+ attrib_idx: c.GLuint,
+ binding_idx: c.GLuint,
+ ) !void {
+ glad.context.VertexAttribBinding.?(attrib_idx, binding_idx);
+ try errors.getError();
+ }
+
+ pub fn attributeFormat(
+ _: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ typ: c.GLenum,
+ normalized: bool,
+ offset: c.GLuint,
+ ) !void {
+ glad.context.VertexAttribFormat.?(
+ idx,
+ size,
+ typ,
+ @intCast(@intFromBool(normalized)),
+ offset,
+ );
+ try errors.getError();
+ }
+
+ pub fn attributeIFormat(
+ _: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ typ: c.GLenum,
+ offset: c.GLuint,
+ ) !void {
+ glad.context.VertexAttribIFormat.?(
+ idx,
+ size,
+ typ,
+ offset,
+ );
+ try errors.getError();
+ }
+
+ pub fn attributeLFormat(
+ _: Binding,
+ idx: c.GLuint,
+ size: c.GLint,
+ offset: c.GLuint,
+ ) !void {
+ glad.context.VertexAttribLFormat.?(
+ idx,
+ size,
+ c.GL_DOUBLE,
+ offset,
+ );
+ try errors.getError();
+ }
+
+ pub fn bindVertexBuffer(
+ _: Binding,
+ idx: c.GLuint,
+ buffer: c.GLuint,
+ offset: c.GLintptr,
+ stride: c.GLsizei,
+ ) !void {
+ glad.context.BindVertexBuffer.?(
+ idx,
+ buffer,
+ offset,
+ stride,
+ );
+ try errors.getError();
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/build.zig b/vendor/libghostty-vt/pkg/opengl/build.zig
new file mode 100644
index 00000000..f472053e
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/build.zig
@@ -0,0 +1,6 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const module = b.addModule("opengl", .{ .root_source_file = b.path("main.zig") });
+ module.addIncludePath(b.path("../../vendor/glad/include"));
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/c.zig b/vendor/libghostty-vt/pkg/opengl/c.zig
new file mode 100644
index 00000000..fc95ee94
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("glad/gl.h");
+});
diff --git a/vendor/libghostty-vt/pkg/opengl/draw.zig b/vendor/libghostty-vt/pkg/opengl/draw.zig
new file mode 100644
index 00000000..50110f60
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/draw.zig
@@ -0,0 +1,94 @@
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+const Primitive = @import("primitives.zig").Primitive;
+
+pub fn clearColor(r: f32, g: f32, b: f32, a: f32) void {
+ glad.context.ClearColor.?(r, g, b, a);
+}
+
+pub fn clear(mask: c.GLbitfield) void {
+ glad.context.Clear.?(mask);
+}
+
+pub fn drawArrays(mode: c.GLenum, first: c.GLint, count: c.GLsizei) !void {
+ glad.context.DrawArrays.?(mode, first, count);
+ try errors.getError();
+}
+
+pub fn drawArraysInstanced(
+ mode: Primitive,
+ first: c.GLint,
+ count: c.GLsizei,
+ primcount: c.GLsizei,
+) !void {
+ glad.context.DrawArraysInstanced.?(
+ @intCast(@intFromEnum(mode)),
+ first,
+ count,
+ primcount,
+ );
+ try errors.getError();
+}
+
+pub fn drawElements(mode: c.GLenum, count: c.GLsizei, typ: c.GLenum, offset: usize) !void {
+ const offsetPtr = if (offset == 0) null else @as(*const anyopaque, @ptrFromInt(offset));
+ glad.context.DrawElements.?(mode, count, typ, offsetPtr);
+ try errors.getError();
+}
+
+pub fn drawElementsInstanced(
+ mode: c.GLenum,
+ count: c.GLsizei,
+ typ: c.GLenum,
+ primcount: c.GLsizei,
+) !void {
+ glad.context.DrawElementsInstanced.?(
+ mode,
+ count,
+ typ,
+ null,
+ primcount,
+ );
+ try errors.getError();
+}
+
+pub fn enable(cap: c.GLenum) !void {
+ glad.context.Enable.?(cap);
+ try errors.getError();
+}
+
+pub fn disable(cap: c.GLenum) !void {
+ glad.context.Disable.?(cap);
+ try errors.getError();
+}
+
+pub fn frontFace(mode: c.GLenum) !void {
+ glad.context.FrontFace.?(mode);
+ try errors.getError();
+}
+
+pub fn blendFunc(sfactor: c.GLenum, dfactor: c.GLenum) !void {
+ glad.context.BlendFunc.?(sfactor, dfactor);
+ try errors.getError();
+}
+
+pub fn viewport(x: c.GLint, y: c.GLint, width: c.GLsizei, height: c.GLsizei) !void {
+ glad.context.Viewport.?(x, y, width, height);
+}
+
+pub fn pixelStore(mode: c.GLenum, value: anytype) !void {
+ switch (@typeInfo(@TypeOf(value))) {
+ .ComptimeInt, .Int => glad.context.PixelStorei.?(mode, value),
+ else => unreachable,
+ }
+ try errors.getError();
+}
+
+pub fn finish() void {
+ glad.context.Finish.?();
+}
+
+pub fn flush() void {
+ glad.context.Flush.?();
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/errors.zig b/vendor/libghostty-vt/pkg/opengl/errors.zig
new file mode 100644
index 00000000..ec046460
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/errors.zig
@@ -0,0 +1,33 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const glad = @import("glad.zig");
+
+pub const Error = error{
+ InvalidEnum,
+ InvalidValue,
+ InvalidOperation,
+ InvalidFramebufferOperation,
+ OutOfMemory,
+
+ Unknown,
+};
+
+/// getError returns the error (if any) from the last OpenGL operation.
+pub fn getError() Error!void {
+ return switch (glad.context.GetError.?()) {
+ c.GL_NO_ERROR => {},
+ c.GL_INVALID_ENUM => Error.InvalidEnum,
+ c.GL_INVALID_VALUE => Error.InvalidValue,
+ c.GL_INVALID_OPERATION => Error.InvalidOperation,
+ c.GL_INVALID_FRAMEBUFFER_OPERATION => Error.InvalidFramebufferOperation,
+ c.GL_OUT_OF_MEMORY => Error.OutOfMemory,
+ else => Error.Unknown,
+ };
+}
+
+/// mustError just calls getError but always results in an error being returned.
+/// If getError has no error, then Unknown is returned.
+pub fn mustError() Error!void {
+ try getError();
+ return Error.Unknown;
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/extensions.zig b/vendor/libghostty-vt/pkg/opengl/extensions.zig
new file mode 100644
index 00000000..5fdbada9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/extensions.zig
@@ -0,0 +1,32 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+const errors = @import("errors.zig");
+const glad = @import("glad.zig");
+
+/// Returns the number of extensions.
+pub fn len() !u32 {
+ var n: c.GLint = undefined;
+ glad.context.GetIntegerv.?(c.GL_NUM_EXTENSIONS, &n);
+ try errors.getError();
+ return @intCast(n);
+}
+
+/// Returns an iterator for the extensions.
+pub fn iterator() !Iterator {
+ return Iterator{ .len = try len() };
+}
+
+/// Iterator for the available extensions.
+pub const Iterator = struct {
+ /// The total number of extensions.
+ len: c.GLuint = 0,
+ i: c.GLuint = 0,
+
+ pub fn next(self: *Iterator) !?[]const u8 {
+ if (self.i >= self.len) return null;
+ const res = glad.context.GetStringi.?(c.GL_EXTENSIONS, self.i);
+ try errors.getError();
+ self.i += 1;
+ return std.mem.sliceTo(res, 0);
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/opengl/glad.zig b/vendor/libghostty-vt/pkg/opengl/glad.zig
new file mode 100644
index 00000000..663e75e1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/glad.zig
@@ -0,0 +1,45 @@
+const std = @import("std");
+const c = @import("c.zig").c;
+
+pub const Context = c.GladGLContext;
+
+/// This is the current context. Set this var manually prior to calling
+/// any of this package's functions. I know its nasty to have a global but
+/// this makes it match OpenGL API styles where it also operates on a
+/// threadlocal global.
+pub threadlocal var context: Context = undefined;
+
+/// Initialize Glad. This is guaranteed to succeed if no errors are returned.
+/// The getProcAddress param is an anytype so that we can accept multiple
+/// forms of the function depending on what we're interfacing with.
+pub fn load(getProcAddress: anytype) !c_int {
+ const GlProc = *const fn () callconv(.c) void;
+ const GlfwFn = *const fn ([*:0]const u8) callconv(.c) ?GlProc;
+
+ const res = switch (@TypeOf(getProcAddress)) {
+ // glfw
+ GlfwFn => c.gladLoadGLContext(&context, @ptrCast(getProcAddress)),
+
+ // null proc address means that we are just loading the globally
+ // pointed gl functions
+ @TypeOf(null) => c.gladLoaderLoadGLContext(&context),
+
+ // try as-is. If this introduces a compiler error, then add a new case.
+ else => c.gladLoadGLContext(&context, @ptrCast(getProcAddress)),
+ };
+ if (res == 0) return error.GLInitFailed;
+ return res;
+}
+
+pub fn unload() void {
+ c.gladLoaderUnloadGLContext(&context);
+ context = undefined;
+}
+
+pub fn versionMajor(res: c_uint) c_uint {
+ return c.GLAD_VERSION_MAJOR(res);
+}
+
+pub fn versionMinor(res: c_uint) c_uint {
+ return c.GLAD_VERSION_MINOR(res);
+}
diff --git a/vendor/libghostty-vt/pkg/opengl/main.zig b/vendor/libghostty-vt/pkg/opengl/main.zig
new file mode 100644
index 00000000..2f22154c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/main.zig
@@ -0,0 +1,45 @@
+//! OpenGL bindings.
+//!
+//! These are purpose-built for usage within this program. While they closely
+//! align with the OpenGL C APIs, they aren't meant to be general purpose,
+//! they aren't meant to have 100% API coverage, and they aren't meant to
+//! be hyper-performant.
+//!
+//! For performance-intensive or unsupported aspects of OpenGL, the C
+//! API is exposed via the `c` constant.
+//!
+//! WARNING: Lots of performance improvements that we can make with Zig
+//! comptime help. I'm deferring this until later but have some fun ideas.
+
+pub const c = @import("c.zig").c;
+pub const glad = @import("glad.zig");
+pub const ext = @import("extensions.zig");
+pub const Buffer = @import("Buffer.zig");
+pub const Framebuffer = @import("Framebuffer.zig");
+pub const Renderbuffer = @import("Renderbuffer.zig");
+pub const Program = @import("Program.zig");
+pub const Sampler = @import("Sampler.zig");
+pub const Shader = @import("Shader.zig");
+pub const Texture = @import("Texture.zig");
+pub const VertexArray = @import("VertexArray.zig");
+
+pub const errors = @import("errors.zig");
+
+pub const Primitive = @import("primitives.zig").Primitive;
+
+const draw = @import("draw.zig");
+
+pub const blendFunc = draw.blendFunc;
+pub const clear = draw.clear;
+pub const clearColor = draw.clearColor;
+pub const drawArrays = draw.drawArrays;
+pub const drawArraysInstanced = draw.drawArraysInstanced;
+pub const drawElements = draw.drawElements;
+pub const drawElementsInstanced = draw.drawElementsInstanced;
+pub const enable = draw.enable;
+pub const disable = draw.disable;
+pub const frontFace = draw.frontFace;
+pub const pixelStore = draw.pixelStore;
+pub const viewport = draw.viewport;
+pub const flush = draw.flush;
+pub const finish = draw.finish;
diff --git a/vendor/libghostty-vt/pkg/opengl/primitives.zig b/vendor/libghostty-vt/pkg/opengl/primitives.zig
new file mode 100644
index 00000000..e12f51a6
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/opengl/primitives.zig
@@ -0,0 +1,18 @@
+pub const c = @import("c.zig").c;
+
+pub const Primitive = enum(c_int) {
+ point = c.GL_POINTS,
+ line = c.GL_LINES,
+ line_strip = c.GL_LINE_STRIP,
+ triangle = c.GL_TRIANGLES,
+ triangle_strip = c.GL_TRIANGLE_STRIP,
+
+ // Commented out primitive types are excluded for parity with Metal.
+ //
+ // line_loop = c.GL_LINE_LOOP,
+ // line_adjacency = c.GL_LINES_ADJACENCY,
+ // line_strip_adjacency = c.GL_LINE_STRIP_ADJACENCY,
+ // triangle_fan = c.GL_TRIANGLE_FAN,
+ // triangle_adjacency = c.GL_TRIANGLES_ADJACENCY,
+ // triangle_strip_adjacency = c.GL_TRIANGLE_STRIP_ADJACENCY,
+};
diff --git a/vendor/libghostty-vt/pkg/sentry/build.zig b/vendor/libghostty-vt/pkg/sentry/build.zig
new file mode 100644
index 00000000..3c88df56
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/build.zig
@@ -0,0 +1,243 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+ const backend = b.option(Backend, "backend", "Backend") orelse .inproc;
+ const transport = b.option(Transport, "transport", "Transport") orelse .none;
+
+ const module = b.addModule("sentry", .{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ });
+
+ const lib = b.addLibrary(.{
+ .name = "sentry",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ if (target.result.os.tag == .windows) {
+ try flags.appendSlice(b.allocator, &.{
+ "-DSENTRY_WITH_UNWINDER_DBGHELP",
+ });
+ } else {
+ try flags.appendSlice(b.allocator, &.{
+ "-DSENTRY_WITH_UNWINDER_LIBBACKTRACE",
+ });
+ }
+ switch (backend) {
+ .crashpad => try flags.append(b.allocator, "-DSENTRY_BACKEND_CRASHPAD"),
+ .breakpad => try flags.append(b.allocator, "-DSENTRY_BACKEND_BREAKPAD"),
+ .inproc => try flags.append(b.allocator, "-DSENTRY_BACKEND_INPROC"),
+ .none => {},
+ }
+
+ if (b.lazyDependency("sentry", .{})) |upstream| {
+ module.addIncludePath(upstream.path("include"));
+ lib.addIncludePath(upstream.path("include"));
+ lib.addIncludePath(upstream.path("src"));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+
+ // Linux-only
+ if (target.result.os.tag == .linux) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "vendor/stb_sprintf.c",
+ },
+ .flags = flags.items,
+ });
+ }
+
+ // Symbolizer + Unwinder
+ if (target.result.os.tag == .windows) {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/sentry_windows_dbghelp.c",
+ "src/path/sentry_path_windows.c",
+ "src/symbolizer/sentry_symbolizer_windows.c",
+ "src/unwinder/sentry_unwinder_dbghelp.c",
+ },
+ .flags = flags.items,
+ });
+ } else {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/sentry_unix_pageallocator.c",
+ "src/path/sentry_path_unix.c",
+ "src/symbolizer/sentry_symbolizer_unix.c",
+ "src/unwinder/sentry_unwinder_libbacktrace.c",
+ },
+ .flags = flags.items,
+ });
+ }
+
+ // Module finder
+ switch (target.result.os.tag) {
+ .windows => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/modulefinder/sentry_modulefinder_windows.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .macos, .ios => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/modulefinder/sentry_modulefinder_apple.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .linux => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/modulefinder/sentry_modulefinder_linux.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .freestanding => {},
+
+ else => {
+ std.log.warn("target={} not supported", .{target.result.os.tag});
+ return error.UnsupportedTarget;
+ },
+ }
+
+ // Transport
+ switch (transport) {
+ .curl => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/transports/sentry_transport_curl.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .winhttp => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/transports/sentry_transport_winhttp.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .none => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/transports/sentry_transport_none.c",
+ },
+ .flags = flags.items,
+ }),
+ }
+
+ // Backend
+ switch (backend) {
+ .crashpad => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/backends/sentry_backend_crashpad.cpp",
+ },
+ .flags = flags.items,
+ }),
+
+ .breakpad => {
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/backends/sentry_backend_breakpad.cpp",
+ },
+ .flags = flags.items,
+ });
+
+ if (b.lazyDependency("breakpad", .{
+ .target = target,
+ .optimize = optimize,
+ })) |breakpad_dep| {
+ lib.linkLibrary(breakpad_dep.artifact("breakpad"));
+
+ // We need to add this because Sentry includes some breakpad
+ // headers that include this vendored file...
+ lib.addIncludePath(breakpad_dep.path("vendor"));
+ }
+ },
+
+ .inproc => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/backends/sentry_backend_inproc.c",
+ },
+ .flags = flags.items,
+ }),
+
+ .none => lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = &.{
+ "src/backends/sentry_backend_none.c",
+ },
+ .flags = flags.items,
+ }),
+ }
+
+ lib.installHeadersDirectory(
+ upstream.path("include"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+}
+
+const srcs: []const []const u8 = &.{
+ "src/sentry_alloc.c",
+ "src/sentry_backend.c",
+ "src/sentry_core.c",
+ "src/sentry_database.c",
+ "src/sentry_envelope.c",
+ "src/sentry_info.c",
+ "src/sentry_json.c",
+ "src/sentry_logger.c",
+ "src/sentry_options.c",
+ "src/sentry_os.c",
+ "src/sentry_random.c",
+ "src/sentry_ratelimiter.c",
+ "src/sentry_scope.c",
+ "src/sentry_session.c",
+ "src/sentry_slice.c",
+ "src/sentry_string.c",
+ "src/sentry_sync.c",
+ "src/sentry_transport.c",
+ "src/sentry_utils.c",
+ "src/sentry_uuid.c",
+ "src/sentry_value.c",
+ "src/sentry_tracing.c",
+ "src/path/sentry_path.c",
+ "src/transports/sentry_disk_transport.c",
+ "src/transports/sentry_function_transport.c",
+ "src/unwinder/sentry_unwinder.c",
+ "vendor/mpack.c",
+};
+
+pub const Backend = enum { crashpad, breakpad, inproc, none };
+pub const Transport = enum { curl, winhttp, none };
diff --git a/vendor/libghostty-vt/pkg/sentry/build.zig.zon b/vendor/libghostty-vt/pkg/sentry/build.zig.zon
new file mode 100644
index 00000000..9c8ed0e2
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .sentry,
+ .version = "0.7.8",
+ .fingerprint = 0xd177b4a12f6b3b79,
+ .paths = .{""},
+ .dependencies = .{
+ // getsentry/sentry-native
+ .sentry = .{
+ .url = "https://deps.files.ghostty.org/sentry-1220446be831adcca918167647c06c7b825849fa3fba5f22da394667974537a9c77e.tar.gz",
+ .hash = "N-V-__8AAPlZGwBEa-gxrcypGBZ2R8Bse4JYSfo_ul8i2jlG",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .breakpad = .{ .path = "../breakpad", .lazy = true },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/sentry/c.zig b/vendor/libghostty-vt/pkg/sentry/c.zig
new file mode 100644
index 00000000..4a0184a8
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("sentry.h");
+});
diff --git a/vendor/libghostty-vt/pkg/sentry/envelope.zig b/vendor/libghostty-vt/pkg/sentry/envelope.zig
new file mode 100644
index 00000000..0d0e38e1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/envelope.zig
@@ -0,0 +1,31 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Value = @import("value.zig").Value;
+
+/// sentry_envelope_t
+pub const Envelope = opaque {
+ pub fn deinit(self: *Envelope) void {
+ c.sentry_envelope_free(@ptrCast(self));
+ }
+
+ pub fn writeToFile(self: *Envelope, path: []const u8) !void {
+ if (c.sentry_envelope_write_to_file_n(
+ @ptrCast(self),
+ path.ptr,
+ path.len,
+ ) != 0) return error.WriteFailed;
+ }
+
+ pub fn serialize(self: *Envelope) []u8 {
+ var len: usize = 0;
+ const ptr = c.sentry_envelope_serialize(@ptrCast(self), &len).?;
+ return ptr[0..len];
+ }
+
+ pub fn event(self: *Envelope) ?Value {
+ const val: Value = .{ .value = c.sentry_envelope_get_event(@ptrCast(self)) };
+ if (val.isNull()) return null;
+ return val;
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/sentry/level.zig b/vendor/libghostty-vt/pkg/sentry/level.zig
new file mode 100644
index 00000000..4b618678
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/level.zig
@@ -0,0 +1,8 @@
+/// sentry_level_t
+pub const Level = enum(c_int) {
+ debug = -1,
+ info = 0,
+ warning = 1,
+ err = 2,
+ fatal = 3,
+};
diff --git a/vendor/libghostty-vt/pkg/sentry/main.zig b/vendor/libghostty-vt/pkg/sentry/main.zig
new file mode 100644
index 00000000..35fc57df
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/main.zig
@@ -0,0 +1,35 @@
+pub const c = @import("c.zig").c;
+
+const transport = @import("transport.zig");
+
+pub const Envelope = @import("envelope.zig").Envelope;
+pub const Level = @import("level.zig").Level;
+pub const Transport = transport.Transport;
+pub const Value = @import("value.zig").Value;
+pub const UUID = @import("uuid.zig").UUID;
+
+pub fn captureEvent(value: Value) ?UUID {
+ const uuid: UUID = .{ .value = c.sentry_capture_event(value.value) };
+ if (uuid.isNil()) return null;
+ return uuid;
+}
+
+pub fn setContext(key: []const u8, value: Value) void {
+ c.sentry_set_context_n(key.ptr, key.len, value.value);
+}
+
+pub fn removeContext(key: []const u8) void {
+ c.sentry_remove_context_n(key.ptr, key.len);
+}
+
+pub fn setTag(key: []const u8, value: []const u8) void {
+ c.sentry_set_tag_n(key.ptr, key.len, value.ptr, value.len);
+}
+
+pub fn free(ptr: *anyopaque) void {
+ c.sentry_free(ptr);
+}
+
+test {
+ @import("std").testing.refAllDecls(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/sentry/transport.zig b/vendor/libghostty-vt/pkg/sentry/transport.zig
new file mode 100644
index 00000000..74718721
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/transport.zig
@@ -0,0 +1,26 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Envelope = @import("envelope.zig").Envelope;
+
+/// sentry_transport_t
+pub const Transport = opaque {
+ pub const SendFunc = *const fn (envelope: *Envelope, state: ?*anyopaque) callconv(.c) void;
+ pub const FreeFunc = *const fn (state: ?*anyopaque) callconv(.c) void;
+
+ pub fn init(f: SendFunc) *Transport {
+ return @ptrCast(c.sentry_transport_new(@ptrCast(f)).?);
+ }
+
+ pub fn deinit(self: *Transport) void {
+ c.sentry_transport_free(@ptrCast(self));
+ }
+
+ pub fn setState(self: *Transport, state: ?*anyopaque) void {
+ c.sentry_transport_set_state(@ptrCast(self), state);
+ }
+
+ pub fn setStateFreeFunc(self: *Transport, f: FreeFunc) void {
+ c.sentry_transport_set_free_func(@ptrCast(self), f);
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/sentry/uuid.zig b/vendor/libghostty-vt/pkg/sentry/uuid.zig
new file mode 100644
index 00000000..07268fda
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/uuid.zig
@@ -0,0 +1,22 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+
+/// sentry_uuid_t
+pub const UUID = struct {
+ value: c.sentry_uuid_t,
+
+ pub fn init() UUID {
+ return .{ .value = c.sentry_uuid_new_v4() };
+ }
+
+ pub fn isNil(self: UUID) bool {
+ return c.sentry_uuid_is_nil(&self.value) != 0;
+ }
+
+ pub fn string(self: UUID) [36:0]u8 {
+ var buf: [36:0]u8 = undefined;
+ c.sentry_uuid_as_string(&self.value, &buf);
+ return buf;
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/sentry/value.zig b/vendor/libghostty-vt/pkg/sentry/value.zig
new file mode 100644
index 00000000..0c839ecd
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/sentry/value.zig
@@ -0,0 +1,75 @@
+const std = @import("std");
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Level = @import("level.zig").Level;
+
+/// sentry_value_t
+pub const Value = struct {
+ /// The underlying value. This is a union that could be represented with
+ /// an extern union but I don't want to risk C ABI issues so we wrap it
+ /// in a struct.
+ value: c.sentry_value_t,
+
+ pub fn initMessageEvent(
+ level: Level,
+ logger: ?[]const u8,
+ message: []const u8,
+ ) Value {
+ return .{ .value = c.sentry_value_new_message_event_n(
+ @intFromEnum(level),
+ if (logger) |v| v.ptr else null,
+ if (logger) |v| v.len else 0,
+ message.ptr,
+ message.len,
+ ) };
+ }
+
+ pub fn initObject() Value {
+ return .{ .value = c.sentry_value_new_object() };
+ }
+
+ pub fn initString(value: []const u8) Value {
+ return .{ .value = c.sentry_value_new_string_n(value.ptr, value.len) };
+ }
+
+ pub fn initBool(value: bool) Value {
+ return .{ .value = c.sentry_value_new_bool(@intFromBool(value)) };
+ }
+
+ pub fn initInt32(value: i32) Value {
+ return .{ .value = c.sentry_value_new_int32(value) };
+ }
+
+ pub fn decref(self: Value) void {
+ c.sentry_value_decref(self.value);
+ }
+
+ pub fn incref(self: Value) Value {
+ c.sentry_value_incref(self.value);
+ }
+
+ pub fn isNull(self: Value) bool {
+ return c.sentry_value_is_null(self.value) != 0;
+ }
+
+ /// sentry_value_set_by_key_n
+ pub fn set(self: Value, key: []const u8, value: Value) void {
+ _ = c.sentry_value_set_by_key_n(
+ self.value,
+ key.ptr,
+ key.len,
+ value.value,
+ );
+ }
+
+ /// sentry_value_set_by_key_n
+ pub fn get(self: Value, key: []const u8) ?Value {
+ const val: Value = .{ .value = c.sentry_value_get_by_key_n(
+ self.value,
+ key.ptr,
+ key.len,
+ ) };
+ if (val.isNull()) return null;
+ return val;
+ }
+};
diff --git a/vendor/libghostty-vt/pkg/simdutf/build.zig b/vendor/libghostty-vt/pkg/simdutf/build.zig
new file mode 100644
index 00000000..e132507a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/simdutf/build.zig
@@ -0,0 +1,81 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const optimize = b.standardOptimizeOption(.{});
+ const target = b.standardTargetOptions(.{});
+
+ const lib = b.addLibrary(.{
+ .name = "simdutf",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+ lib.addIncludePath(b.path("vendor"));
+
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (target.result.abi.isAndroid()) {
+ const android_ndk = @import("android_ndk");
+ try android_ndk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ // Zig 0.13 bug: https://github.com/ziglang/zig/issues/20414
+ // (See root Ghostty build.zig on why we do this)
+ try flags.appendSlice(b.allocator, &.{
+ "-DSIMDUTF_IMPLEMENTATION_ICELAKE=0",
+
+ // Fixes linker issues for release builds missing ubsanitizer symbols
+ "-fno-sanitize=undefined",
+ "-fno-sanitize-trap=undefined",
+ });
+
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ lib.addCSourceFiles(.{
+ .flags = flags.items,
+ .files = &.{
+ "vendor/simdutf.cpp",
+ },
+ });
+ lib.installHeadersDirectory(
+ b.path("vendor"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+
+ b.installArtifact(lib);
+
+ // {
+ // const test_exe = b.addTest(.{
+ // .name = "test",
+ // .root_source_file = .{ .path = "main.zig" },
+ // .target = target,
+ // .optimize = optimize,
+ // });
+ // test_exe.linkLibrary(lib);
+ //
+ // var it = module.import_table.iterator();
+ // while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
+ // const tests_run = b.addRunArtifact(test_exe);
+ // const test_step = b.step("test", "Run tests");
+ // test_step.dependOn(&tests_run.step);
+ // }
+}
diff --git a/vendor/libghostty-vt/pkg/simdutf/build.zig.zon b/vendor/libghostty-vt/pkg/simdutf/build.zig.zon
new file mode 100644
index 00000000..afbef541
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/simdutf/build.zig.zon
@@ -0,0 +1,10 @@
+.{
+ .name = .simdutf,
+ .version = "5.2.8",
+ .fingerprint = 0x7494ad640528a0d,
+ .paths = .{""},
+ .dependencies = .{
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .android_ndk = .{ .path = "../android-ndk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.cpp b/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.cpp
new file mode 100644
index 00000000..84e4459c
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.cpp
@@ -0,0 +1,38579 @@
+/* auto-generated on 2024-05-07 22:33:11 -0400. Do not edit! */
+/* begin file src/simdutf.cpp */
+#include "simdutf.h"
+// We include base64_tables once.
+/* begin file src/tables/base64_tables.h */
+#ifndef SIMDUTF_BASE64_TABLES_H
+#define SIMDUTF_BASE64_TABLES_H
+#include <array>
+#include <cstdint>
+
+namespace simdutf {
+namespace {
+namespace tables {
+namespace base64 {
+namespace base64_default {
+
+const char e0[256] = {
+ 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D',
+ 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H',
+ 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L',
+ 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O',
+ 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S',
+ 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W',
+ 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a',
+ 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd',
+ 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h',
+ 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l',
+ 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p',
+ 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's',
+ 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w',
+ 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0',
+ '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4',
+ '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7',
+ '8', '8', '8', '8', '9', '9', '9', '9', '+', '+', '+', '+', '/', '/', '/',
+ '/'};
+
+const char e1[256] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C',
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+',
+ '/'};
+
+const char e2[256] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/', 'A', 'B', 'C',
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+',
+ '/'};
+
+const uint32_t d0[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
+ 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
+ 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
+ 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
+ 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
+ 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
+ 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
+ 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
+ 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
+ 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
+ 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+
+const uint32_t d1[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
+ 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
+ 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
+ 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
+ 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
+ 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
+ 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
+ 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
+ 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
+ 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
+ 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+
+const uint32_t d2[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
+ 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
+ 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
+ 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
+ 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
+ 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
+ 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
+ 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
+ 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
+ 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
+ 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+
+const uint32_t d3[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
+ 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
+ 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
+ 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
+ 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
+ 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
+ 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
+ 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
+ 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
+ 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
+ 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+} // namespace base64_default
+
+namespace base64_url {
+
+const char e0[256] = {
+ 'A', 'A', 'A', 'A', 'B', 'B', 'B', 'B', 'C', 'C', 'C', 'C', 'D', 'D', 'D',
+ 'D', 'E', 'E', 'E', 'E', 'F', 'F', 'F', 'F', 'G', 'G', 'G', 'G', 'H', 'H',
+ 'H', 'H', 'I', 'I', 'I', 'I', 'J', 'J', 'J', 'J', 'K', 'K', 'K', 'K', 'L',
+ 'L', 'L', 'L', 'M', 'M', 'M', 'M', 'N', 'N', 'N', 'N', 'O', 'O', 'O', 'O',
+ 'P', 'P', 'P', 'P', 'Q', 'Q', 'Q', 'Q', 'R', 'R', 'R', 'R', 'S', 'S', 'S',
+ 'S', 'T', 'T', 'T', 'T', 'U', 'U', 'U', 'U', 'V', 'V', 'V', 'V', 'W', 'W',
+ 'W', 'W', 'X', 'X', 'X', 'X', 'Y', 'Y', 'Y', 'Y', 'Z', 'Z', 'Z', 'Z', 'a',
+ 'a', 'a', 'a', 'b', 'b', 'b', 'b', 'c', 'c', 'c', 'c', 'd', 'd', 'd', 'd',
+ 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g', 'g', 'h', 'h', 'h',
+ 'h', 'i', 'i', 'i', 'i', 'j', 'j', 'j', 'j', 'k', 'k', 'k', 'k', 'l', 'l',
+ 'l', 'l', 'm', 'm', 'm', 'm', 'n', 'n', 'n', 'n', 'o', 'o', 'o', 'o', 'p',
+ 'p', 'p', 'p', 'q', 'q', 'q', 'q', 'r', 'r', 'r', 'r', 's', 's', 's', 's',
+ 't', 't', 't', 't', 'u', 'u', 'u', 'u', 'v', 'v', 'v', 'v', 'w', 'w', 'w',
+ 'w', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'z', 'z', 'z', 'z', '0', '0',
+ '0', '0', '1', '1', '1', '1', '2', '2', '2', '2', '3', '3', '3', '3', '4',
+ '4', '4', '4', '5', '5', '5', '5', '6', '6', '6', '6', '7', '7', '7', '7',
+ '8', '8', '8', '8', '9', '9', '9', '9', '-', '-', '-', '-', '_', '_', '_',
+ '_'};
+
+const char e1[256] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C',
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-',
+ '_'};
+
+const char e2[256] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
+ 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
+ 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
+ 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3',
+ '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C', 'D', 'E', 'F', 'G',
+ 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
+ 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k',
+ 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_', 'A', 'B', 'C',
+ 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
+ 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g',
+ 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
+ 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-',
+ '_'};
+
+const uint32_t d0[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000f8, 0x01ffffff, 0x01ffffff,
+ 0x000000d0, 0x000000d4, 0x000000d8, 0x000000dc, 0x000000e0, 0x000000e4,
+ 0x000000e8, 0x000000ec, 0x000000f0, 0x000000f4, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00000004, 0x00000008, 0x0000000c, 0x00000010, 0x00000014, 0x00000018,
+ 0x0000001c, 0x00000020, 0x00000024, 0x00000028, 0x0000002c, 0x00000030,
+ 0x00000034, 0x00000038, 0x0000003c, 0x00000040, 0x00000044, 0x00000048,
+ 0x0000004c, 0x00000050, 0x00000054, 0x00000058, 0x0000005c, 0x00000060,
+ 0x00000064, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x000000fc,
+ 0x01ffffff, 0x00000068, 0x0000006c, 0x00000070, 0x00000074, 0x00000078,
+ 0x0000007c, 0x00000080, 0x00000084, 0x00000088, 0x0000008c, 0x00000090,
+ 0x00000094, 0x00000098, 0x0000009c, 0x000000a0, 0x000000a4, 0x000000a8,
+ 0x000000ac, 0x000000b0, 0x000000b4, 0x000000b8, 0x000000bc, 0x000000c0,
+ 0x000000c4, 0x000000c8, 0x000000cc, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+const uint32_t d1[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000e003, 0x01ffffff, 0x01ffffff,
+ 0x00004003, 0x00005003, 0x00006003, 0x00007003, 0x00008003, 0x00009003,
+ 0x0000a003, 0x0000b003, 0x0000c003, 0x0000d003, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00001000, 0x00002000, 0x00003000, 0x00004000, 0x00005000, 0x00006000,
+ 0x00007000, 0x00008000, 0x00009000, 0x0000a000, 0x0000b000, 0x0000c000,
+ 0x0000d000, 0x0000e000, 0x0000f000, 0x00000001, 0x00001001, 0x00002001,
+ 0x00003001, 0x00004001, 0x00005001, 0x00006001, 0x00007001, 0x00008001,
+ 0x00009001, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x0000f003,
+ 0x01ffffff, 0x0000a001, 0x0000b001, 0x0000c001, 0x0000d001, 0x0000e001,
+ 0x0000f001, 0x00000002, 0x00001002, 0x00002002, 0x00003002, 0x00004002,
+ 0x00005002, 0x00006002, 0x00007002, 0x00008002, 0x00009002, 0x0000a002,
+ 0x0000b002, 0x0000c002, 0x0000d002, 0x0000e002, 0x0000f002, 0x00000003,
+ 0x00001003, 0x00002003, 0x00003003, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+const uint32_t d2[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00800f00, 0x01ffffff, 0x01ffffff,
+ 0x00000d00, 0x00400d00, 0x00800d00, 0x00c00d00, 0x00000e00, 0x00400e00,
+ 0x00800e00, 0x00c00e00, 0x00000f00, 0x00400f00, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00400000, 0x00800000, 0x00c00000, 0x00000100, 0x00400100, 0x00800100,
+ 0x00c00100, 0x00000200, 0x00400200, 0x00800200, 0x00c00200, 0x00000300,
+ 0x00400300, 0x00800300, 0x00c00300, 0x00000400, 0x00400400, 0x00800400,
+ 0x00c00400, 0x00000500, 0x00400500, 0x00800500, 0x00c00500, 0x00000600,
+ 0x00400600, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00c00f00,
+ 0x01ffffff, 0x00800600, 0x00c00600, 0x00000700, 0x00400700, 0x00800700,
+ 0x00c00700, 0x00000800, 0x00400800, 0x00800800, 0x00c00800, 0x00000900,
+ 0x00400900, 0x00800900, 0x00c00900, 0x00000a00, 0x00400a00, 0x00800a00,
+ 0x00c00a00, 0x00000b00, 0x00400b00, 0x00800b00, 0x00c00b00, 0x00000c00,
+ 0x00400c00, 0x00800c00, 0x00c00c00, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+const uint32_t d3[256] = {
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003e0000, 0x01ffffff, 0x01ffffff,
+ 0x00340000, 0x00350000, 0x00360000, 0x00370000, 0x00380000, 0x00390000,
+ 0x003a0000, 0x003b0000, 0x003c0000, 0x003d0000, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x00000000,
+ 0x00010000, 0x00020000, 0x00030000, 0x00040000, 0x00050000, 0x00060000,
+ 0x00070000, 0x00080000, 0x00090000, 0x000a0000, 0x000b0000, 0x000c0000,
+ 0x000d0000, 0x000e0000, 0x000f0000, 0x00100000, 0x00110000, 0x00120000,
+ 0x00130000, 0x00140000, 0x00150000, 0x00160000, 0x00170000, 0x00180000,
+ 0x00190000, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x003f0000,
+ 0x01ffffff, 0x001a0000, 0x001b0000, 0x001c0000, 0x001d0000, 0x001e0000,
+ 0x001f0000, 0x00200000, 0x00210000, 0x00220000, 0x00230000, 0x00240000,
+ 0x00250000, 0x00260000, 0x00270000, 0x00280000, 0x00290000, 0x002a0000,
+ 0x002b0000, 0x002c0000, 0x002d0000, 0x002e0000, 0x002f0000, 0x00300000,
+ 0x00310000, 0x00320000, 0x00330000, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff,
+ 0x01ffffff, 0x01ffffff, 0x01ffffff, 0x01ffffff};
+} // namespace base64_url
+const uint64_t thintable_epi8[256] = {
+ 0x0706050403020100, 0x0007060504030201, 0x0007060504030200,
+ 0x0000070605040302, 0x0007060504030100, 0x0000070605040301,
+ 0x0000070605040300, 0x0000000706050403, 0x0007060504020100,
+ 0x0000070605040201, 0x0000070605040200, 0x0000000706050402,
+ 0x0000070605040100, 0x0000000706050401, 0x0000000706050400,
+ 0x0000000007060504, 0x0007060503020100, 0x0000070605030201,
+ 0x0000070605030200, 0x0000000706050302, 0x0000070605030100,
+ 0x0000000706050301, 0x0000000706050300, 0x0000000007060503,
+ 0x0000070605020100, 0x0000000706050201, 0x0000000706050200,
+ 0x0000000007060502, 0x0000000706050100, 0x0000000007060501,
+ 0x0000000007060500, 0x0000000000070605, 0x0007060403020100,
+ 0x0000070604030201, 0x0000070604030200, 0x0000000706040302,
+ 0x0000070604030100, 0x0000000706040301, 0x0000000706040300,
+ 0x0000000007060403, 0x0000070604020100, 0x0000000706040201,
+ 0x0000000706040200, 0x0000000007060402, 0x0000000706040100,
+ 0x0000000007060401, 0x0000000007060400, 0x0000000000070604,
+ 0x0000070603020100, 0x0000000706030201, 0x0000000706030200,
+ 0x0000000007060302, 0x0000000706030100, 0x0000000007060301,
+ 0x0000000007060300, 0x0000000000070603, 0x0000000706020100,
+ 0x0000000007060201, 0x0000000007060200, 0x0000000000070602,
+ 0x0000000007060100, 0x0000000000070601, 0x0000000000070600,
+ 0x0000000000000706, 0x0007050403020100, 0x0000070504030201,
+ 0x0000070504030200, 0x0000000705040302, 0x0000070504030100,
+ 0x0000000705040301, 0x0000000705040300, 0x0000000007050403,
+ 0x0000070504020100, 0x0000000705040201, 0x0000000705040200,
+ 0x0000000007050402, 0x0000000705040100, 0x0000000007050401,
+ 0x0000000007050400, 0x0000000000070504, 0x0000070503020100,
+ 0x0000000705030201, 0x0000000705030200, 0x0000000007050302,
+ 0x0000000705030100, 0x0000000007050301, 0x0000000007050300,
+ 0x0000000000070503, 0x0000000705020100, 0x0000000007050201,
+ 0x0000000007050200, 0x0000000000070502, 0x0000000007050100,
+ 0x0000000000070501, 0x0000000000070500, 0x0000000000000705,
+ 0x0000070403020100, 0x0000000704030201, 0x0000000704030200,
+ 0x0000000007040302, 0x0000000704030100, 0x0000000007040301,
+ 0x0000000007040300, 0x0000000000070403, 0x0000000704020100,
+ 0x0000000007040201, 0x0000000007040200, 0x0000000000070402,
+ 0x0000000007040100, 0x0000000000070401, 0x0000000000070400,
+ 0x0000000000000704, 0x0000000703020100, 0x0000000007030201,
+ 0x0000000007030200, 0x0000000000070302, 0x0000000007030100,
+ 0x0000000000070301, 0x0000000000070300, 0x0000000000000703,
+ 0x0000000007020100, 0x0000000000070201, 0x0000000000070200,
+ 0x0000000000000702, 0x0000000000070100, 0x0000000000000701,
+ 0x0000000000000700, 0x0000000000000007, 0x0006050403020100,
+ 0x0000060504030201, 0x0000060504030200, 0x0000000605040302,
+ 0x0000060504030100, 0x0000000605040301, 0x0000000605040300,
+ 0x0000000006050403, 0x0000060504020100, 0x0000000605040201,
+ 0x0000000605040200, 0x0000000006050402, 0x0000000605040100,
+ 0x0000000006050401, 0x0000000006050400, 0x0000000000060504,
+ 0x0000060503020100, 0x0000000605030201, 0x0000000605030200,
+ 0x0000000006050302, 0x0000000605030100, 0x0000000006050301,
+ 0x0000000006050300, 0x0000000000060503, 0x0000000605020100,
+ 0x0000000006050201, 0x0000000006050200, 0x0000000000060502,
+ 0x0000000006050100, 0x0000000000060501, 0x0000000000060500,
+ 0x0000000000000605, 0x0000060403020100, 0x0000000604030201,
+ 0x0000000604030200, 0x0000000006040302, 0x0000000604030100,
+ 0x0000000006040301, 0x0000000006040300, 0x0000000000060403,
+ 0x0000000604020100, 0x0000000006040201, 0x0000000006040200,
+ 0x0000000000060402, 0x0000000006040100, 0x0000000000060401,
+ 0x0000000000060400, 0x0000000000000604, 0x0000000603020100,
+ 0x0000000006030201, 0x0000000006030200, 0x0000000000060302,
+ 0x0000000006030100, 0x0000000000060301, 0x0000000000060300,
+ 0x0000000000000603, 0x0000000006020100, 0x0000000000060201,
+ 0x0000000000060200, 0x0000000000000602, 0x0000000000060100,
+ 0x0000000000000601, 0x0000000000000600, 0x0000000000000006,
+ 0x0000050403020100, 0x0000000504030201, 0x0000000504030200,
+ 0x0000000005040302, 0x0000000504030100, 0x0000000005040301,
+ 0x0000000005040300, 0x0000000000050403, 0x0000000504020100,
+ 0x0000000005040201, 0x0000000005040200, 0x0000000000050402,
+ 0x0000000005040100, 0x0000000000050401, 0x0000000000050400,
+ 0x0000000000000504, 0x0000000503020100, 0x0000000005030201,
+ 0x0000000005030200, 0x0000000000050302, 0x0000000005030100,
+ 0x0000000000050301, 0x0000000000050300, 0x0000000000000503,
+ 0x0000000005020100, 0x0000000000050201, 0x0000000000050200,
+ 0x0000000000000502, 0x0000000000050100, 0x0000000000000501,
+ 0x0000000000000500, 0x0000000000000005, 0x0000000403020100,
+ 0x0000000004030201, 0x0000000004030200, 0x0000000000040302,
+ 0x0000000004030100, 0x0000000000040301, 0x0000000000040300,
+ 0x0000000000000403, 0x0000000004020100, 0x0000000000040201,
+ 0x0000000000040200, 0x0000000000000402, 0x0000000000040100,
+ 0x0000000000000401, 0x0000000000000400, 0x0000000000000004,
+ 0x0000000003020100, 0x0000000000030201, 0x0000000000030200,
+ 0x0000000000000302, 0x0000000000030100, 0x0000000000000301,
+ 0x0000000000000300, 0x0000000000000003, 0x0000000000020100,
+ 0x0000000000000201, 0x0000000000000200, 0x0000000000000002,
+ 0x0000000000000100, 0x0000000000000001, 0x0000000000000000,
+ 0x0000000000000000,
+};
+
+const uint8_t pshufb_combine_table[272] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0x00, 0x01, 0x02, 0x03,
+ 0x04, 0x05, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff,
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0x00, 0x01, 0x02, 0x08,
+ 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0x00, 0x01, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0x00, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x08, 0x09, 0x0a, 0x0b,
+ 0x0c, 0x0d, 0x0e, 0x0f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+const unsigned char BitsSetTable256mul2[256] = {
+ 0, 2, 2, 4, 2, 4, 4, 6, 2, 4, 4, 6, 4, 6, 6, 8, 2, 4, 4,
+ 6, 4, 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 2, 4, 4, 6, 4, 6,
+ 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6,
+ 8, 8, 10, 8, 10, 10, 12, 2, 4, 4, 6, 4, 6, 6, 8, 4, 6, 6, 8,
+ 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10,
+ 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6, 8,
+ 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 2, 4, 4, 6, 4,
+ 6, 6, 8, 4, 6, 6, 8, 6, 8, 8, 10, 4, 6, 6, 8, 6, 8, 8, 10,
+ 6, 8, 8, 10, 8, 10, 10, 12, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8,
+ 10, 8, 10, 10, 12, 6, 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12,
+ 12, 14, 4, 6, 6, 8, 6, 8, 8, 10, 6, 8, 8, 10, 8, 10, 10, 12, 6,
+ 8, 8, 10, 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 6, 8, 8, 10,
+ 8, 10, 10, 12, 8, 10, 10, 12, 10, 12, 12, 14, 8, 10, 10, 12, 10, 12, 12,
+ 14, 10, 12, 12, 14, 12, 14, 14, 16};
+
+constexpr uint8_t to_base64_value[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255,
+ 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255};
+
+constexpr uint8_t to_base64_url_value[] = {
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 64, 64, 255, 64, 64, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 64, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 62, 255, 255, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255,
+ 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
+ 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
+ 25, 255, 255, 255, 255, 63, 255, 26, 27, 28, 29, 30, 31, 32, 33,
+ 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48,
+ 49, 50, 51, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
+ 255};
+static_assert(sizeof(to_base64_value) == 256, "to_base64_value must have 256 elements");
+static_assert(sizeof(to_base64_url_value) == 256, "to_base64_url_value must have 256 elements");
+static_assert(to_base64_value[uint8_t(' ')] == 64, "space must be == 64 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t(' ')] == 64, "space must be == 64 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('\t')] == 64, "tab must be == 64 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('\t')] == 64, "tab must be == 64 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('\r')] == 64, "cr must be == 64 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('\r')] == 64, "cr must be == 64 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('\n')] == 64, "lf must be == 64 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('\n')] == 64, "lf must be == 64 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('\f')] == 64, "ff must be == 64 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('\f')] == 64, "ff must be == 64 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('+')] == 62, "+ must be == 62 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('-')] == 62, "- must be == 62 in to_base64_url_value");
+static_assert(to_base64_value[uint8_t('/')] == 63, "/ must be == 62 in to_base64_value");
+static_assert(to_base64_url_value[uint8_t('_')] == 63, "_ must be == 62 in to_base64_url_value");
+} // namespace base64
+} // namespace tables
+} // unnamed namespace
+} // namespace simdutf
+
+#endif // SIMDUTF_BASE64_TABLES_H
+/* end file src/tables/base64_tables.h */
+/* begin file src/implementation.cpp */
+#include <initializer_list>
+#include <climits>
+#include <type_traits>
+
+// Useful for debugging purposes
+namespace simdutf {
+namespace {
+
+template <typename T>
+std::string toBinaryString(T b) {
+ std::string binary = "";
+ T mask = T(1) << (sizeof(T) * CHAR_BIT - 1);
+ while (mask > 0) {
+ binary += ((b & mask) == 0) ? '0' : '1';
+ mask >>= 1;
+ }
+ return binary;
+}
+}
+}
+
+// Implementations
+// The best choice should always come first!
+/* begin file src/simdutf/arm64.h */
+#ifndef SIMDUTF_ARM64_H
+#define SIMDUTF_ARM64_H
+
+#ifdef SIMDUTF_FALLBACK_H
+#error "arm64.h must be included before fallback.h"
+#endif
+
+
+#ifndef SIMDUTF_IMPLEMENTATION_ARM64
+#define SIMDUTF_IMPLEMENTATION_ARM64 (SIMDUTF_IS_ARM64)
+#endif
+#if SIMDUTF_IMPLEMENTATION_ARM64 && SIMDUTF_IS_ARM64
+#define SIMDUTF_CAN_ALWAYS_RUN_ARM64 1
+#else
+#define SIMDUTF_CAN_ALWAYS_RUN_ARM64 0
+#endif
+
+
+
+#if SIMDUTF_IMPLEMENTATION_ARM64
+
+namespace simdutf {
+/**
+ * Implementation for NEON (ARMv8).
+ */
+namespace arm64 {
+} // namespace arm64
+} // namespace simdutf
+
+/* begin file src/simdutf/arm64/implementation.h */
+#ifndef SIMDUTF_ARM64_IMPLEMENTATION_H
+#define SIMDUTF_ARM64_IMPLEMENTATION_H
+
+
+namespace simdutf {
+namespace arm64 {
+
+namespace {
+using namespace simdutf;
+}
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation() : simdutf::implementation("arm64", "ARM NEON", internal::instruction_set::NEON) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+
+} // namespace arm64
+} // namespace simdutf
+
+#endif // SIMDUTF_ARM64_IMPLEMENTATION_H
+/* end file src/simdutf/arm64/implementation.h */
+
+/* begin file src/simdutf/arm64/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "arm64"
+// #define SIMDUTF_IMPLEMENTATION arm64
+/* end file src/simdutf/arm64/begin.h */
+
+// Declarations
+/* begin file src/simdutf/arm64/intrinsics.h */
+#ifndef SIMDUTF_ARM64_INTRINSICS_H
+#define SIMDUTF_ARM64_INTRINSICS_H
+
+
+// This should be the correct header whether
+// you use visual studio or other compilers.
+#include <arm_neon.h>
+
+#endif // SIMDUTF_ARM64_INTRINSICS_H
+/* end file src/simdutf/arm64/intrinsics.h */
+/* begin file src/simdutf/arm64/bitmanipulation.h */
+#ifndef SIMDUTF_ARM64_BITMANIPULATION_H
+#define SIMDUTF_ARM64_BITMANIPULATION_H
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+
+/* result might be undefined when input_num is zero */
+simdutf_really_inline int count_ones(uint64_t input_num) {
+ return vaddv_u8(vcnt_u8(vcreate_u8(input_num)));
+}
+
+#if SIMDUTF_NEED_TRAILING_ZEROES
+simdutf_really_inline int trailing_zeroes(uint64_t input_num) {
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ unsigned long ret;
+ // Search the mask data from least significant bit (LSB)
+ // to the most significant bit (MSB) for a set bit (1).
+ _BitScanForward64(&ret, input_num);
+ return (int)ret;
+#else // SIMDUTF_REGULAR_VISUAL_STUDIO
+ return __builtin_ctzll(input_num);
+#endif // SIMDUTF_REGULAR_VISUAL_STUDIO
+}
+#endif
+
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+
+#endif // SIMDUTF_ARM64_BITMANIPULATION_H
+/* end file src/simdutf/arm64/bitmanipulation.h */
+/* begin file src/simdutf/arm64/simd.h */
+#ifndef SIMDUTF_ARM64_SIMD_H
+#define SIMDUTF_ARM64_SIMD_H
+
+#include <type_traits>
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace simd {
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+namespace {
+// Start of private section with Visual Studio workaround
+
+#ifndef simdutf_make_uint8x16_t
+#define simdutf_make_uint8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
+ x13, x14, x15, x16) \
+ ([=]() { \
+ uint8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
+ x9, x10, x11, x12, x13, x14, x15, x16}; \
+ return vld1q_u8(array); \
+ }())
+#endif
+#ifndef simdutf_make_int8x16_t
+#define simdutf_make_int8x16_t(x1, x2, x3, x4, x5, x6, x7, x8, x9, x10, x11, x12, \
+ x13, x14, x15, x16) \
+ ([=]() { \
+ int8_t array[16] = {x1, x2, x3, x4, x5, x6, x7, x8, \
+ x9, x10, x11, x12, x13, x14, x15, x16}; \
+ return vld1q_s8(array); \
+ }())
+#endif
+
+#ifndef simdutf_make_uint8x8_t
+#define simdutf_make_uint8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
+ ([=]() { \
+ uint8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
+ return vld1_u8(array); \
+ }())
+#endif
+#ifndef simdutf_make_int8x8_t
+#define simdutf_make_int8x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
+ ([=]() { \
+ int8_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
+ return vld1_s8(array); \
+ }())
+#endif
+#ifndef simdutf_make_uint16x8_t
+#define simdutf_make_uint16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
+ ([=]() { \
+ uint16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
+ return vld1q_u16(array); \
+ }())
+#endif
+#ifndef simdutf_make_int16x8_t
+#define simdutf_make_int16x8_t(x1, x2, x3, x4, x5, x6, x7, x8) \
+ ([=]() { \
+ int16_t array[8] = {x1, x2, x3, x4, x5, x6, x7, x8}; \
+ return vld1q_s16(array); \
+ }())
+#endif
+
+
+// End of private section with Visual Studio workaround
+} // namespace
+#endif // SIMDUTF_REGULAR_VISUAL_STUDIO
+
+
+ template<typename T>
+ struct simd8;
+
+ //
+ // Base class of simd8<uint8_t> and simd8<bool>, both of which use uint8x16_t internally.
+ //
+ template<typename T, typename Mask=simd8<bool>>
+ struct base_u8 {
+ uint8x16_t value;
+ static const int SIZE = sizeof(value);
+
+ // Conversion from/to SIMD register
+ simdutf_really_inline base_u8(const uint8x16_t _value) : value(_value) {}
+ simdutf_really_inline operator const uint8x16_t&() const { return this->value; }
+ simdutf_really_inline operator uint8x16_t&() { return this->value; }
+ simdutf_really_inline T first() const { return vgetq_lane_u8(*this,0); }
+ simdutf_really_inline T last() const { return vgetq_lane_u8(*this,15); }
+
+ // Bit operations
+ simdutf_really_inline simd8<T> operator|(const simd8<T> other) const { return vorrq_u8(*this, other); }
+ simdutf_really_inline simd8<T> operator&(const simd8<T> other) const { return vandq_u8(*this, other); }
+ simdutf_really_inline simd8<T> operator^(const simd8<T> other) const { return veorq_u8(*this, other); }
+ simdutf_really_inline simd8<T> bit_andnot(const simd8<T> other) const { return vbicq_u8(*this, other); }
+ simdutf_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
+ simdutf_really_inline simd8<T>& operator|=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
+ simdutf_really_inline simd8<T>& operator&=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
+ simdutf_really_inline simd8<T>& operator^=(const simd8<T> other) { auto this_cast = static_cast<simd8<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
+
+ friend simdutf_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return vceqq_u8(lhs, rhs); }
+
+ template<int N=1>
+ simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
+ return vextq_u8(prev_chunk, *this, 16 - N);
+ }
+ };
+
+ // SIMD byte mask type (returned by things like eq and gt)
+ template<>
+ struct simd8<bool>: base_u8<bool> {
+ typedef uint16_t bitmask_t;
+ typedef uint32_t bitmask2_t;
+
+ static simdutf_really_inline simd8<bool> splat(bool _value) { return vmovq_n_u8(uint8_t(-(!!_value))); }
+
+ simdutf_really_inline simd8(const uint8x16_t _value) : base_u8<bool>(_value) {}
+ // False constructor
+ simdutf_really_inline simd8() : simd8(vdupq_n_u8(0)) {}
+ // Splat constructor
+ simdutf_really_inline simd8(bool _value) : simd8(splat(_value)) {}
+ simdutf_really_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); }
+
+ // We return uint32_t instead of uint16_t because that seems to be more efficient for most
+ // purposes (cutting it down to uint16_t costs performance in some compilers).
+ simdutf_really_inline uint32_t to_bitmask() const {
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t bit_mask = simdutf_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
+#else
+ const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+#endif
+ auto minput = *this & bit_mask;
+ uint8x16_t tmp = vpaddq_u8(minput, minput);
+ tmp = vpaddq_u8(tmp, tmp);
+ tmp = vpaddq_u8(tmp, tmp);
+ return vgetq_lane_u16(vreinterpretq_u16_u8(tmp), 0);
+ }
+
+ // Returns 4-bit out of each byte, alternating between the high 4 bits and low bits
+ // result it is 64 bit.
+ // This method is expected to be faster than none() and is equivalent
+ // when the vector register is the result of a comparison, with byte
+ // values 0xff and 0x00.
+ simdutf_really_inline uint64_t to_bitmask64() const {
+ return vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(*this), 4)), 0);
+ }
+
+ simdutf_really_inline bool any() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) != 0; }
+ simdutf_really_inline bool none() const { return vmaxvq_u32(vreinterpretq_u32_u8(*this)) == 0; }
+ simdutf_really_inline bool all() const { return vminvq_u32(vreinterpretq_u32_u8(*this)) == 0xFFFFF; }
+
+
+ };
+
+ // Unsigned bytes
+ template<>
+ struct simd8<uint8_t>: base_u8<uint8_t> {
+ static simdutf_really_inline simd8<uint8_t> splat(uint8_t _value) { return vmovq_n_u8(_value); }
+ static simdutf_really_inline simd8<uint8_t> zero() { return vdupq_n_u8(0); }
+ static simdutf_really_inline simd8<uint8_t> load(const uint8_t* values) { return vld1q_u8(values); }
+ simdutf_really_inline simd8(const uint8x16_t _value) : base_u8<uint8_t>(_value) {}
+ // Zero constructor
+ simdutf_really_inline simd8() : simd8(zero()) {}
+ // Array constructor
+ simdutf_really_inline simd8(const uint8_t values[16]) : simd8(load(values)) {}
+ // Splat constructor
+ simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
+ // Member-by-member initialization
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ simdutf_really_inline simd8(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) : simd8(simdutf_make_uint8x16_t(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ )) {}
+#else
+ simdutf_really_inline simd8(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) : simd8(uint8x16_t{
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ }) {}
+#endif
+
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<uint8_t> repeat_16(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) {
+ return simd8<uint8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+ // Store to array
+ simdutf_really_inline void store(uint8_t dst[16]) const { return vst1q_u8(dst, *this); }
+
+ // Saturated math
+ simdutf_really_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return vqaddq_u8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return vqsubq_u8(*this, other); }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd8<uint8_t> operator+(const simd8<uint8_t> other) const { return vaddq_u8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> operator-(const simd8<uint8_t> other) const { return vsubq_u8(*this, other); }
+ simdutf_really_inline simd8<uint8_t>& operator+=(const simd8<uint8_t> other) { *this = *this + other; return *this; }
+ simdutf_really_inline simd8<uint8_t>& operator-=(const simd8<uint8_t> other) { *this = *this - other; return *this; }
+
+ // Order-specific operations
+ simdutf_really_inline uint8_t max_val() const { return vmaxvq_u8(*this); }
+ simdutf_really_inline uint8_t min_val() const { return vminvq_u8(*this); }
+ simdutf_really_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return vmaxq_u8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return vminq_u8(*this, other); }
+ simdutf_really_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return vcleq_u8(*this, other); }
+ simdutf_really_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return vcgeq_u8(*this, other); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return vcltq_u8(*this, other); }
+ simdutf_really_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return vcgtq_u8(*this, other); }
+ // Same as >, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
+ simdutf_really_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this > other); }
+ // Same as <, but instead of guaranteeing all 1's == true, false = 0 and true = nonzero. For ARM, returns all 1's.
+ simdutf_really_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return simd8<uint8_t>(*this < other); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return vtstq_u8(*this, bits); }
+ simdutf_really_inline bool is_ascii() const { return this->max_val() < 0b10000000u; }
+
+ simdutf_really_inline bool any_bits_set_anywhere() const { return this->max_val() != 0; }
+ simdutf_really_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return (*this & bits).any_bits_set_anywhere(); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shr() const { return vshrq_n_u8(*this, N); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shl() const { return vshlq_n_u8(*this, N); }
+
+ // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
+ return lookup_table.apply_lookup_16_to(*this);
+ }
+
+
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(
+ L replace0, L replace1, L replace2, L replace3,
+ L replace4, L replace5, L replace6, L replace7,
+ L replace8, L replace9, L replace10, L replace11,
+ L replace12, L replace13, L replace14, L replace15) const {
+ return lookup_16(simd8<L>::repeat_16(
+ replace0, replace1, replace2, replace3,
+ replace4, replace5, replace6, replace7,
+ replace8, replace9, replace10, replace11,
+ replace12, replace13, replace14, replace15
+ ));
+ }
+
+ template<typename T>
+ simdutf_really_inline simd8<uint8_t> apply_lookup_16_to(const simd8<T> original) const {
+ return vqtbl1q_u8(*this, simd8<uint8_t>(original));
+ }
+ };
+
+ // Signed bytes
+ template<>
+ struct simd8<int8_t> {
+ int8x16_t value;
+
+ static simdutf_really_inline simd8<int8_t> splat(int8_t _value) { return vmovq_n_s8(_value); }
+ static simdutf_really_inline simd8<int8_t> zero() { return vdupq_n_s8(0); }
+ static simdutf_really_inline simd8<int8_t> load(const int8_t values[16]) { return vld1q_s8(values); }
+
+ // Use ST2 instead of UXTL+UXTL2 to interleave zeroes. UXTL is actually a USHLL #0,
+ // and shifting in NEON is actually quite slow.
+ //
+ // While this needs the registers to be in a specific order, bigger cores can interleave
+ // these with no overhead, and it still performs decently on little cores.
+ // movi v1.3d, #0
+ // mov v0.16b, value[0]
+ // st2 {v0.16b, v1.16b}, [ptr], #32
+ // mov v0.16b, value[1]
+ // st2 {v0.16b, v1.16b}, [ptr], #32
+ // ...
+ template <endianness big_endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * p) const {
+ int8x16x2_t pair = match_system(big_endian)
+ ? int8x16x2_t{{this->value, vmovq_n_s8(0)}}
+ : int8x16x2_t{{vmovq_n_s8(0), this->value}};
+ vst2q_s8(reinterpret_cast<int8_t *>(p), pair);
+ }
+
+ // currently unused
+ // Technically this could be done with ST4 like in store_ascii_as_utf16, but it is
+ // very much not worth it, as explicitly mentioned in the ARM Cortex-X1 Core Software
+ // Optimization Guide:
+ // 4.18 Complex ASIMD instructions
+ // The bandwidth of [ST4 with element size less than 64b] is limited by decode
+ // constraints and it is advisable to avoid them when high performing code is desired.
+ // Instead, it is better to use ZIP1+ZIP2 and two ST2.
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * p) const {
+ const uint16x8_t low = vreinterpretq_u16_s8(vzip1q_s8(this->value, vmovq_n_s8(0)));
+ const uint16x8_t high = vreinterpretq_u16_s8(vzip2q_s8(this->value, vmovq_n_s8(0)));
+ const uint16x8x2_t low_pair{{ low, vmovq_n_u16(0) }};
+ vst2q_u16(reinterpret_cast<uint16_t *>(p), low_pair);
+ const uint16x8x2_t high_pair{{ high, vmovq_n_u16(0) }};
+ vst2q_u16(reinterpret_cast<uint16_t *>(p + 8), high_pair);
+ }
+
+ // In places where the table can be reused, which is most uses in simdutf, it is worth it to do
+ // 4 table lookups, as there is no direct zero extension from u8 to u32.
+ simdutf_really_inline void store_ascii_as_utf32_tbl(char32_t * p) const {
+ const simd8<uint8_t> tb1{ 0,255,255,255, 1,255,255,255, 2,255,255,255, 3,255,255,255 };
+ const simd8<uint8_t> tb2{ 4,255,255,255, 5,255,255,255, 6,255,255,255, 7,255,255,255 };
+ const simd8<uint8_t> tb3{ 8,255,255,255, 9,255,255,255, 10,255,255,255, 11,255,255,255 };
+ const simd8<uint8_t> tb4{ 12,255,255,255, 13,255,255,255, 14,255,255,255, 15,255,255,255 };
+
+ // encourage store pairing and interleaving
+ const auto shuf1 = this->apply_lookup_16_to(tb1);
+ const auto shuf2 = this->apply_lookup_16_to(tb2);
+ shuf1.store(reinterpret_cast<int8_t *>(p));
+ shuf2.store(reinterpret_cast<int8_t *>(p + 4));
+
+ const auto shuf3 = this->apply_lookup_16_to(tb3);
+ const auto shuf4 = this->apply_lookup_16_to(tb4);
+ shuf3.store(reinterpret_cast<int8_t *>(p + 8));
+ shuf4.store(reinterpret_cast<int8_t *>(p + 12));
+ }
+ // Conversion from/to SIMD register
+ simdutf_really_inline simd8(const int8x16_t _value) : value{_value} {}
+ simdutf_really_inline operator const int8x16_t&() const { return this->value; }
+#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO
+ simdutf_really_inline operator const uint8x16_t() const { return vreinterpretq_u8_s8(this->value); }
+#endif
+ simdutf_really_inline operator int8x16_t&() { return this->value; }
+
+ // Zero constructor
+ simdutf_really_inline simd8() : simd8(zero()) {}
+ // Splat constructor
+ simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const int8_t* values) : simd8(load(values)) {}
+ // Member-by-member initialization
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ simdutf_really_inline simd8(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) : simd8(simdutf_make_int8x16_t(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ )) {}
+#else
+ simdutf_really_inline simd8(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) : simd8(int8x16_t{
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ }) {}
+#endif
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<int8_t> repeat_16(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) {
+ return simd8<int8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+ // Store to array
+ simdutf_really_inline void store(int8_t dst[16]) const { return vst1q_s8(dst, value); }
+ // Explicit conversion to/from unsigned
+ //
+ // Under Visual Studio/ARM64 uint8x16_t and int8x16_t are apparently the same type.
+ // In theory, we could check this occurrence with std::same_as and std::enabled_if but it is C++14
+ // and relatively ugly and hard to read.
+#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO
+ simdutf_really_inline explicit simd8(const uint8x16_t other): simd8(vreinterpretq_s8_u8(other)) {}
+#endif
+ simdutf_really_inline operator simd8<uint8_t>() const { return vreinterpretq_u8_s8(this->value); }
+
+ simdutf_really_inline simd8<int8_t> operator|(const simd8<int8_t> other) const { return vorrq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t> operator&(const simd8<int8_t> other) const { return vandq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t> operator^(const simd8<int8_t> other) const { return veorq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t> bit_andnot(const simd8<int8_t> other) const { return vbicq_s8(value, other.value); }
+
+ // Math
+ simdutf_really_inline simd8<int8_t> operator+(const simd8<int8_t> other) const { return vaddq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t> operator-(const simd8<int8_t> other) const { return vsubq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t>& operator+=(const simd8<int8_t> other) { *this = *this + other; return *this; }
+ simdutf_really_inline simd8<int8_t>& operator-=(const simd8<int8_t> other) { *this = *this - other; return *this; }
+
+ simdutf_really_inline int8_t max_val() const { return vmaxvq_s8(value); }
+ simdutf_really_inline int8_t min_val() const { return vminvq_s8(value); }
+ simdutf_really_inline bool is_ascii() const { return this->min_val() >= 0; }
+
+ // Order-sensitive comparisons
+ simdutf_really_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return vmaxq_s8(value, other.value); }
+ simdutf_really_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return vminq_s8(value, other.value); }
+ simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const { return vcgtq_s8(value, other.value); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const { return vcltq_s8(value, other.value); }
+ simdutf_really_inline simd8<bool> operator==(const simd8<int8_t> other) const { return vceqq_s8(value, other.value); }
+
+ template<int N=1>
+ simdutf_really_inline simd8<int8_t> prev(const simd8<int8_t> prev_chunk) const {
+ return vextq_s8(prev_chunk, *this, 16 - N);
+ }
+
+ // Perform a lookup assuming no value is larger than 16
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
+ return lookup_table.apply_lookup_16_to(*this);
+ }
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(
+ L replace0, L replace1, L replace2, L replace3,
+ L replace4, L replace5, L replace6, L replace7,
+ L replace8, L replace9, L replace10, L replace11,
+ L replace12, L replace13, L replace14, L replace15) const {
+ return lookup_16(simd8<L>::repeat_16(
+ replace0, replace1, replace2, replace3,
+ replace4, replace5, replace6, replace7,
+ replace8, replace9, replace10, replace11,
+ replace12, replace13, replace14, replace15
+ ));
+ }
+
+ template<typename T>
+ simdutf_really_inline simd8<int8_t> apply_lookup_16_to(const simd8<T> original) const {
+ return vqtbl1q_s8(*this, simd8<uint8_t>(original));
+ }
+ };
+
+ template<typename T>
+ struct simd8x64 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
+ static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
+ simd8<T> chunks[NUM_CHUNKS];
+
+ simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
+ simd8x64<T>& operator=(const simd8<T> other) = delete; // no assignment allowed
+ simd8x64() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
+ simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+2*sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+3*sizeof(simd8<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd8<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd8<T>)*1/sizeof(T));
+ this->chunks[2].store(ptr+sizeof(simd8<T>)*2/sizeof(T));
+ this->chunks[3].store(ptr+sizeof(simd8<T>)*3/sizeof(T));
+ }
+
+
+ simdutf_really_inline simd8x64<T>& operator |=(const simd8x64<T> &other) {
+ this->chunks[0] |= other.chunks[0];
+ this->chunks[1] |= other.chunks[1];
+ this->chunks[2] |= other.chunks[2];
+ this->chunks[3] |= other.chunks[3];
+ return *this;
+ }
+
+ simdutf_really_inline simd8<T> reduce_or() const {
+ return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return reduce_or().is_ascii();
+ }
+
+ template <endianness endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*1);
+ this->chunks[2].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*2);
+ this->chunks[3].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*3);
+ }
+
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf32_tbl(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].store_ascii_as_utf32_tbl(ptr+sizeof(simd8<T>)*1);
+ this->chunks[2].store_ascii_as_utf32_tbl(ptr+sizeof(simd8<T>)*2);
+ this->chunks[3].store_ascii_as_utf32_tbl(ptr+sizeof(simd8<T>)*3);
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t bit_mask = simdutf_make_uint8x16_t(
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
+ );
+#else
+ const uint8x16_t bit_mask = {
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
+ };
+#endif
+ // Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
+ uint8x16_t sum0 = vpaddq_u8(vandq_u8(uint8x16_t(this->chunks[0]), bit_mask), vandq_u8(uint8x16_t(this->chunks[1]), bit_mask));
+ uint8x16_t sum1 = vpaddq_u8(vandq_u8(uint8x16_t(this->chunks[2]), bit_mask), vandq_u8(uint8x16_t(this->chunks[3]), bit_mask));
+ sum0 = vpaddq_u8(sum0, sum1);
+ sum0 = vpaddq_u8(sum0, sum0);
+ return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask,
+ this->chunks[2] == mask,
+ this->chunks[3] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask,
+ this->chunks[2] <= mask,
+ this->chunks[3] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+
+ return simd8x64<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low),
+ (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low),
+ (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+ return simd8x64<bool>(
+ (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low),
+ (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low),
+ (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low),
+ (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask,
+ this->chunks[2] < mask,
+ this->chunks[3] < mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] > mask,
+ this->chunks[1] > mask,
+ this->chunks[2] > mask,
+ this->chunks[3] > mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] >= mask,
+ this->chunks[1] >= mask,
+ this->chunks[2] >= mask,
+ this->chunks[3] >= mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
+ const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
+ return simd8x64<bool>(
+ simd8<uint8_t>(uint8x16_t(this->chunks[0])) >= mask,
+ simd8<uint8_t>(uint8x16_t(this->chunks[1])) >= mask,
+ simd8<uint8_t>(uint8x16_t(this->chunks[2])) >= mask,
+ simd8<uint8_t>(uint8x16_t(this->chunks[3])) >= mask
+ ).to_bitmask();
+ }
+ }; // struct simd8x64<T>
+/* begin file src/simdutf/arm64/simd16-inl.h */
+template<typename T>
+struct simd16;
+
+ template<typename T, typename Mask=simd16<bool>>
+ struct base_u16 {
+ uint16x8_t value;
+ static const int SIZE = sizeof(value);
+
+ // Conversion from/to SIMD register
+ simdutf_really_inline base_u16() = default;
+ simdutf_really_inline base_u16(const uint16x8_t _value) : value(_value) {}
+ simdutf_really_inline operator const uint16x8_t&() const { return this->value; }
+ simdutf_really_inline operator uint16x8_t&() { return this->value; }
+ // Bit operations
+ simdutf_really_inline simd16<T> operator|(const simd16<T> other) const { return vorrq_u16(*this, other); }
+ simdutf_really_inline simd16<T> operator&(const simd16<T> other) const { return vandq_u16(*this, other); }
+ simdutf_really_inline simd16<T> operator^(const simd16<T> other) const { return veorq_u16(*this, other); }
+ simdutf_really_inline simd16<T> bit_andnot(const simd16<T> other) const { return vbicq_u16(*this, other); }
+ simdutf_really_inline simd16<T> operator~() const { return *this ^ 0xFFu; }
+ simdutf_really_inline simd16<T>& operator|=(const simd16<T> other) { auto this_cast = static_cast<simd16<T>*>(this); *this_cast = *this_cast | other; return *this_cast; }
+ simdutf_really_inline simd16<T>& operator&=(const simd16<T> other) { auto this_cast = static_cast<simd16<T>*>(this); *this_cast = *this_cast & other; return *this_cast; }
+ simdutf_really_inline simd16<T>& operator^=(const simd16<T> other) { auto this_cast = static_cast<simd16<T>*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
+
+ friend simdutf_really_inline Mask operator==(const simd16<T> lhs, const simd16<T> rhs) { return vceqq_u16(lhs, rhs); }
+
+ template<int N=1>
+ simdutf_really_inline simd16<T> prev(const simd16<T> prev_chunk) const {
+ return vextq_u18(prev_chunk, *this, 8 - N);
+ }
+ };
+
+template<typename T, typename Mask=simd16<bool>>
+struct base16: base_u16<T> {
+ typedef uint16_t bitmask_t;
+ typedef uint32_t bitmask2_t;
+
+ simdutf_really_inline base16() : base_u16<T>() {}
+ simdutf_really_inline base16(const uint16x8_t _value) : base_u16<T>(_value) {}
+ template <typename Pointer>
+ simdutf_really_inline base16(const Pointer* ptr) : base16(vld1q_u16(ptr)) {}
+
+ static const int SIZE = sizeof(base_u16<T>::value);
+
+ template<int N=1>
+ simdutf_really_inline simd16<T> prev(const simd16<T> prev_chunk) const {
+ return vextq_u18(prev_chunk, *this, 8 - N);
+ }
+};
+
+// SIMD byte mask type (returned by things like eq and gt)
+template<>
+struct simd16<bool>: base16<bool> {
+ static simdutf_really_inline simd16<bool> splat(bool _value) { return vmovq_n_u16(uint16_t(-(!!_value))); }
+
+ simdutf_really_inline simd16<bool>() : base16() {}
+ simdutf_really_inline simd16<bool>(const uint16x8_t _value) : base16<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd16<bool>(bool _value) : base16<bool>(splat(_value)) {}
+
+};
+
+template<typename T>
+struct base16_numeric: base16<T> {
+ static simdutf_really_inline simd16<T> splat(T _value) { return vmovq_n_u16(_value); }
+ static simdutf_really_inline simd16<T> zero() { return vdupq_n_u16(0); }
+ static simdutf_really_inline simd16<T> load(const T values[8]) {
+ return vld1q_u16(reinterpret_cast<const uint16_t*>(values));
+ }
+
+ simdutf_really_inline base16_numeric() : base16<T>() {}
+ simdutf_really_inline base16_numeric(const uint16x8_t _value) : base16<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[8]) const { return vst1q_u16(dst, *this); }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd16<T> operator~() const { return *this ^ 0xFFu; }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd16<T> operator+(const simd16<T> other) const { return vaddq_u8(*this, other); }
+ simdutf_really_inline simd16<T> operator-(const simd16<T> other) const { return vsubq_u8(*this, other); }
+ simdutf_really_inline simd16<T>& operator+=(const simd16<T> other) { *this = *this + other; return *static_cast<simd16<T>*>(this); }
+ simdutf_really_inline simd16<T>& operator-=(const simd16<T> other) { *this = *this - other; return *static_cast<simd16<T>*>(this); }
+};
+
+// Signed code units
+template<>
+struct simd16<int16_t> : base16_numeric<int16_t> {
+ simdutf_really_inline simd16() : base16_numeric<int16_t>() {}
+#ifndef SIMDUTF_REGULAR_VISUAL_STUDIO
+ simdutf_really_inline simd16(const uint16x8_t _value) : base16_numeric<int16_t>(_value) {}
+#endif
+ simdutf_really_inline simd16(const int16x8_t _value) : base16_numeric<int16_t>(vreinterpretq_u16_s16(_value)) {}
+
+ // Splat constructor
+ simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const int16_t*>(values))) {}
+ simdutf_really_inline operator simd16<uint16_t>() const;
+ simdutf_really_inline operator const uint16x8_t&() const { return this->value; }
+ simdutf_really_inline operator const int16x8_t() const { return vreinterpretq_s16_u16(this->value); }
+
+ simdutf_really_inline int16_t max_val() const { return vmaxvq_s16(vreinterpretq_s16_u16(this->value)); }
+ simdutf_really_inline int16_t min_val() const { return vminvq_s16(vreinterpretq_s16_u16(this->value)); }
+ // Order-sensitive comparisons
+ simdutf_really_inline simd16<int16_t> max_val(const simd16<int16_t> other) const { return vmaxq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); }
+ simdutf_really_inline simd16<int16_t> min_val(const simd16<int16_t> other) const { return vmaxq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); }
+ simdutf_really_inline simd16<bool> operator>(const simd16<int16_t> other) const { return vcgtq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<int16_t> other) const { return vcltq_s16(vreinterpretq_s16_u16(this->value), vreinterpretq_s16_u16(other.value)); }
+};
+
+
+
+
+// Unsigned code units
+template<>
+struct simd16<uint16_t>: base16_numeric<uint16_t> {
+ simdutf_really_inline simd16() : base16_numeric<uint16_t>() {}
+ simdutf_really_inline simd16(const uint16x8_t _value) : base16_numeric<uint16_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const uint16_t*>(values))) {}
+
+
+ simdutf_really_inline int16_t max_val() const { return vmaxvq_u16(*this); }
+ simdutf_really_inline int16_t min_val() const { return vminvq_u16(*this); }
+ // Saturated math
+ simdutf_really_inline simd16<uint16_t> saturating_add(const simd16<uint16_t> other) const { return vqaddq_u16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> saturating_sub(const simd16<uint16_t> other) const { return vqsubq_u16(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd16<uint16_t> max_val(const simd16<uint16_t> other) const { return vmaxq_u16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> min_val(const simd16<uint16_t> other) const { return vminq_u16(*this, other); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> gt_bits(const simd16<uint16_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> lt_bits(const simd16<uint16_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd16<bool> operator<=(const simd16<uint16_t> other) const { return vcleq_u16(*this, other); }
+ simdutf_really_inline simd16<bool> operator>=(const simd16<uint16_t> other) const { return vcgeq_u16(*this, other); }
+ simdutf_really_inline simd16<bool> operator>(const simd16<uint16_t> other) const { return vcgtq_u16(*this, other); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<uint16_t> other) const { return vcltq_u16(*this, other); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd16<bool> bits_not_set() const { return *this == uint16_t(0); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shr() const { return simd16<uint16_t>(vshrq_n_u16(*this, N)); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shl() const { return simd16<uint16_t>(vshlq_n_u16(*this, N)); }
+
+ // logical operations
+ simdutf_really_inline simd16<uint16_t> operator|(const simd16<uint16_t> other) const { return vorrq_u16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> operator&(const simd16<uint16_t> other) const { return vandq_u16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> operator^(const simd16<uint16_t> other) const { return veorq_u16(*this, other); }
+
+ // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector
+ static simdutf_really_inline simd8<uint8_t> pack(const simd16<uint16_t>& v0, const simd16<uint16_t>& v1) {
+ return vqmovn_high_u16(vqmovn_u16(v0), v1);
+ }
+
+ // Change the endianness
+ simdutf_really_inline simd16<uint16_t> swap_bytes() const {
+ return vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(*this)));
+ }
+};
+simdutf_really_inline simd16<int16_t>::operator simd16<uint16_t>() const { return this->value; }
+
+
+ template<typename T>
+ struct simd16x32 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd16<T>);
+ static_assert(NUM_CHUNKS == 4, "ARM kernel should use four registers per 64-byte block.");
+ simd16<T> chunks[NUM_CHUNKS];
+
+ simd16x32(const simd16x32<T>& o) = delete; // no copy allowed
+ simd16x32<T>& operator=(const simd16<T> other) = delete; // no assignment allowed
+ simd16x32() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd16x32(const simd16<T> chunk0, const simd16<T> chunk1, const simd16<T> chunk2, const simd16<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
+ simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16<T>::load(ptr), simd16<T>::load(ptr+sizeof(simd16<T>)/sizeof(T)), simd16<T>::load(ptr+2*sizeof(simd16<T>)/sizeof(T)), simd16<T>::load(ptr+3*sizeof(simd16<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd16<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd16<T>)*1/sizeof(T));
+ this->chunks[2].store(ptr+sizeof(simd16<T>)*2/sizeof(T));
+ this->chunks[3].store(ptr+sizeof(simd16<T>)*3/sizeof(T));
+ }
+
+ simdutf_really_inline simd16<T> reduce_or() const {
+ return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return reduce_or().is_ascii();
+ }
+
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*0);
+ this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*1);
+ this->chunks[2].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*2);
+ this->chunks[3].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*3);
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t bit_mask = simdutf_make_uint8x16_t(
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
+ );
+#else
+ const uint8x16_t bit_mask = {
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80
+ };
+#endif
+ // Add each of the elements next to each other, successively, to stuff each 8 byte mask into one.
+ uint8x16_t sum0 = vpaddq_u8(vreinterpretq_u8_u16(this->chunks[0] & vreinterpretq_u16_u8(bit_mask)), vreinterpretq_u8_u16(this->chunks[1] & vreinterpretq_u16_u8(bit_mask)));
+ uint8x16_t sum1 = vpaddq_u8(vreinterpretq_u8_u16(this->chunks[2] & vreinterpretq_u16_u8(bit_mask)), vreinterpretq_u8_u16(this->chunks[3] & vreinterpretq_u16_u8(bit_mask)));
+ sum0 = vpaddq_u8(sum0, sum1);
+ sum0 = vpaddq_u8(sum0, sum0);
+ return vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
+ }
+
+ simdutf_really_inline void swap_bytes() {
+ this->chunks[0] = this->chunks[0].swap_bytes();
+ this->chunks[1] = this->chunks[1].swap_bytes();
+ this->chunks[2] = this->chunks[2].swap_bytes();
+ this->chunks[3] = this->chunks[3].swap_bytes();
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask,
+ this->chunks[2] == mask,
+ this->chunks[3] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask,
+ this->chunks[2] <= mask,
+ this->chunks[3] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(low);
+ const simd16<T> mask_high = simd16<T>::splat(high);
+
+ return simd16x32<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low),
+ (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low),
+ (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(low);
+ const simd16<T> mask_high = simd16<T>::splat(high);
+ return simd16x32<bool>(
+ (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low),
+ (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low),
+ (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low),
+ (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask,
+ this->chunks[2] < mask,
+ this->chunks[3] < mask
+ ).to_bitmask();
+ }
+
+ }; // struct simd16x32<T>
+ template<>
+ simdutf_really_inline uint64_t simd16x32<uint16_t>::not_in_range(const uint16_t low, const uint16_t high) const {
+ const simd16<uint16_t> mask_low = simd16<uint16_t>::splat(low);
+ const simd16<uint16_t> mask_high = simd16<uint16_t>::splat(high);
+ simd16x32<uint16_t> x(
+ simd16<uint16_t>((this->chunks[0] > mask_high) | (this->chunks[0] < mask_low)),
+ simd16<uint16_t>((this->chunks[1] > mask_high) | (this->chunks[1] < mask_low)),
+ simd16<uint16_t>((this->chunks[2] > mask_high) | (this->chunks[2] < mask_low)),
+ simd16<uint16_t>((this->chunks[3] > mask_high) | (this->chunks[3] < mask_low))
+ );
+ return x.to_bitmask();
+ }
+/* end file src/simdutf/arm64/simd16-inl.h */
+} // namespace simd
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+
+#endif // SIMDUTF_ARM64_SIMD_H
+/* end file src/simdutf/arm64/simd.h */
+
+/* begin file src/simdutf/arm64/end.h */
+/* end file src/simdutf/arm64/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_ARM64
+
+#endif // SIMDUTF_ARM64_H
+/* end file src/simdutf/arm64.h */
+/* begin file src/simdutf/icelake.h */
+#ifndef SIMDUTF_ICELAKE_H
+#define SIMDUTF_ICELAKE_H
+
+
+
+#ifdef __has_include
+// How do we detect that a compiler supports vbmi2?
+// For sure if the following header is found, we are ok?
+#if __has_include(<avx512vbmi2intrin.h>)
+#define SIMDUTF_COMPILER_SUPPORTS_VBMI2 1
+#endif
+#endif
+
+#ifdef _MSC_VER
+#if _MSC_VER >= 1930
+// Visual Studio 2022 and up support VBMI2 under x64 even if the header
+// avx512vbmi2intrin.h is not found.
+// Visual Studio 2019 technically supports VBMI2, but the implementation
+// might be unreliable. Search for visualstudio2019icelakeissue in our
+// tests.
+#define SIMDUTF_COMPILER_SUPPORTS_VBMI2 1
+#endif
+#endif
+
+// We allow icelake on x64 as long as the compiler is known to support VBMI2.
+#ifndef SIMDUTF_IMPLEMENTATION_ICELAKE
+#define SIMDUTF_IMPLEMENTATION_ICELAKE ((SIMDUTF_IS_X86_64) && (SIMDUTF_COMPILER_SUPPORTS_VBMI2))
+#endif
+
+// To see why (__BMI__) && (__LZCNT__) are not part of this next line, see
+// https://github.com/simdutf/simdutf/issues/1247
+#if ((SIMDUTF_IMPLEMENTATION_ICELAKE) && (SIMDUTF_IS_X86_64) && (__AVX2__) && (SIMDUTF_HAS_AVX512F && \
+ SIMDUTF_HAS_AVX512DQ && \
+ SIMDUTF_HAS_AVX512VL && \
+ SIMDUTF_HAS_AVX512VBMI2) && (!SIMDUTF_IS_32BITS))
+#define SIMDUTF_CAN_ALWAYS_RUN_ICELAKE 1
+#else
+#define SIMDUTF_CAN_ALWAYS_RUN_ICELAKE 0
+#endif
+
+#if SIMDUTF_IMPLEMENTATION_ICELAKE
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+#define SIMDUTF_TARGET_ICELAKE
+#else
+#define SIMDUTF_TARGET_ICELAKE SIMDUTF_TARGET_REGION("avx512f,avx512dq,avx512cd,avx512bw,avx512vbmi,avx512vbmi2,avx512vl,avx2,bmi,bmi2,pclmul,lzcnt,popcnt,avx512vpopcntdq")
+#endif
+
+namespace simdutf {
+namespace icelake {
+} // namespace icelake
+} // namespace simdutf
+
+
+
+//
+// These two need to be included outside SIMDUTF_TARGET_REGION
+//
+/* begin file src/simdutf/icelake/intrinsics.h */
+#ifndef SIMDUTF_ICELAKE_INTRINSICS_H
+#define SIMDUTF_ICELAKE_INTRINSICS_H
+
+
+#ifdef SIMDUTF_VISUAL_STUDIO
+// under clang within visual studio, this will include <x86intrin.h>
+#include <intrin.h> // visual studio or clang
+#include <immintrin.h>
+#else
+
+#if SIMDUTF_GCC11ORMORE
+// We should not get warnings while including <x86intrin.h> yet we do
+// under some versions of GCC.
+// If the x86intrin.h header has uninitialized values that are problematic,
+// it is a GCC issue, we want to ignore these warnings.
+SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized)
+#endif
+
+#include <x86intrin.h> // elsewhere
+
+
+#if SIMDUTF_GCC11ORMORE
+// cancels the suppression of the -Wuninitialized
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif
+
+#ifndef _tzcnt_u64
+#define _tzcnt_u64(x) __tzcnt_u64(x)
+#endif // _tzcnt_u64
+#endif // SIMDUTF_VISUAL_STUDIO
+
+#ifdef SIMDUTF_CLANG_VISUAL_STUDIO
+/**
+ * You are not supposed, normally, to include these
+ * headers directly. Instead you should either include intrin.h
+ * or x86intrin.h. However, when compiling with clang
+ * under Windows (i.e., when _MSC_VER is set), these headers
+ * only get included *if* the corresponding features are detected
+ * from macros:
+ * e.g., if __AVX2__ is set... in turn, we normally set these
+ * macros by compiling against the corresponding architecture
+ * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
+ * software with these advanced instructions. In simdutf, we
+ * want to compile the whole program for a generic target,
+ * and only target our specific kernels. As a workaround,
+ * we directly include the needed headers. These headers would
+ * normally guard against such usage, but we carefully included
+ * <x86intrin.h> (or <intrin.h>) before, so the headers
+ * are fooled.
+ */
+#include <bmiintrin.h> // for _blsr_u64
+#include <bmi2intrin.h> // for _pext_u64, _pdep_u64
+#include <lzcntintrin.h> // for __lzcnt64
+#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
+#include <smmintrin.h>
+#include <tmmintrin.h>
+#include <avxintrin.h>
+#include <avx2intrin.h>
+// Important: we need the AVX-512 headers:
+#include <avx512fintrin.h>
+#include <avx512dqintrin.h>
+#include <avx512cdintrin.h>
+#include <avx512bwintrin.h>
+#include <avx512vlintrin.h>
+#include <avx512vlbwintrin.h>
+#include <avx512vbmiintrin.h>
+#include <avx512vbmi2intrin.h>
+#include <avx512vpopcntdqintrin.h>
+#include <avx512vpopcntdqvlintrin.h>
+// unfortunately, we may not get _blsr_u64, but, thankfully, clang
+// has it as a macro.
+#ifndef _blsr_u64
+// we roll our own
+#define _blsr_u64(n) ((n - 1) & n)
+#endif // _blsr_u64
+#endif // SIMDUTF_CLANG_VISUAL_STUDIO
+
+
+
+#if defined(__GNUC__) && !defined(__clang__)
+
+#if __GNUC__ == 8
+#define SIMDUTF_GCC8 1
+#elif __GNUC__ == 9
+#define SIMDUTF_GCC9 1
+#endif // __GNUC__ == 8 || __GNUC__ == 9
+
+#endif // defined(__GNUC__) && !defined(__clang__)
+
+#if SIMDUTF_GCC8
+#pragma GCC push_options
+#pragma GCC target("avx512f")
+/**
+ * GCC 8 fails to provide _mm512_set_epi8. We roll our own.
+ */
+inline __m512i _mm512_set_epi8(uint8_t a0, uint8_t a1, uint8_t a2, uint8_t a3, uint8_t a4, uint8_t a5, uint8_t a6, uint8_t a7, uint8_t a8, uint8_t a9, uint8_t a10, uint8_t a11, uint8_t a12, uint8_t a13, uint8_t a14, uint8_t a15, uint8_t a16, uint8_t a17, uint8_t a18, uint8_t a19, uint8_t a20, uint8_t a21, uint8_t a22, uint8_t a23, uint8_t a24, uint8_t a25, uint8_t a26, uint8_t a27, uint8_t a28, uint8_t a29, uint8_t a30, uint8_t a31, uint8_t a32, uint8_t a33, uint8_t a34, uint8_t a35, uint8_t a36, uint8_t a37, uint8_t a38, uint8_t a39, uint8_t a40, uint8_t a41, uint8_t a42, uint8_t a43, uint8_t a44, uint8_t a45, uint8_t a46, uint8_t a47, uint8_t a48, uint8_t a49, uint8_t a50, uint8_t a51, uint8_t a52, uint8_t a53, uint8_t a54, uint8_t a55, uint8_t a56, uint8_t a57, uint8_t a58, uint8_t a59, uint8_t a60, uint8_t a61, uint8_t a62, uint8_t a63) {
+ return _mm512_set_epi64(uint64_t(a7) + (uint64_t(a6) << 8) + (uint64_t(a5) << 16) + (uint64_t(a4) << 24) + (uint64_t(a3) << 32) + (uint64_t(a2) << 40) + (uint64_t(a1) << 48) + (uint64_t(a0) << 56),
+ uint64_t(a15) + (uint64_t(a14) << 8) + (uint64_t(a13) << 16) + (uint64_t(a12) << 24) + (uint64_t(a11) << 32) + (uint64_t(a10) << 40) + (uint64_t(a9) << 48) + (uint64_t(a8) << 56),
+ uint64_t(a23) + (uint64_t(a22) << 8) + (uint64_t(a21) << 16) + (uint64_t(a20) << 24) + (uint64_t(a19) << 32) + (uint64_t(a18) << 40) + (uint64_t(a17) << 48) + (uint64_t(a16) << 56),
+ uint64_t(a31) + (uint64_t(a30) << 8) + (uint64_t(a29) << 16) + (uint64_t(a28) << 24) + (uint64_t(a27) << 32) + (uint64_t(a26) << 40) + (uint64_t(a25) << 48) + (uint64_t(a24) << 56),
+ uint64_t(a39) + (uint64_t(a38) << 8) + (uint64_t(a37) << 16) + (uint64_t(a36) << 24) + (uint64_t(a35) << 32) + (uint64_t(a34) << 40) + (uint64_t(a33) << 48) + (uint64_t(a32) << 56),
+ uint64_t(a47) + (uint64_t(a46) << 8) + (uint64_t(a45) << 16) + (uint64_t(a44) << 24) + (uint64_t(a43) << 32) + (uint64_t(a42) << 40) + (uint64_t(a41) << 48) + (uint64_t(a40) << 56),
+ uint64_t(a55) + (uint64_t(a54) << 8) + (uint64_t(a53) << 16) + (uint64_t(a52) << 24) + (uint64_t(a51) << 32) + (uint64_t(a50) << 40) + (uint64_t(a49) << 48) + (uint64_t(a48) << 56),
+ uint64_t(a63) + (uint64_t(a62) << 8) + (uint64_t(a61) << 16) + (uint64_t(a60) << 24) + (uint64_t(a59) << 32) + (uint64_t(a58) << 40) + (uint64_t(a57) << 48) + (uint64_t(a56) << 56));
+}
+#pragma GCC pop_options
+#endif // SIMDUTF_GCC8
+
+#endif // SIMDUTF_HASWELL_INTRINSICS_H
+/* end file src/simdutf/icelake/intrinsics.h */
+/* begin file src/simdutf/icelake/implementation.h */
+#ifndef SIMDUTF_ICELAKE_IMPLEMENTATION_H
+#define SIMDUTF_ICELAKE_IMPLEMENTATION_H
+
+
+namespace simdutf {
+namespace icelake {
+
+namespace {
+using namespace simdutf;
+}
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation() : simdutf::implementation(
+ "icelake",
+ "Intel AVX512 (AVX-512BW, AVX-512CD, AVX-512VL, AVX-512VBMI2 extensions)",
+ internal::instruction_set::AVX2 | internal::instruction_set::BMI1 | internal::instruction_set::BMI2 | internal::instruction_set::AVX512BW | internal::instruction_set::AVX512CD | internal::instruction_set::AVX512VL | internal::instruction_set::AVX512VBMI2 | internal::instruction_set::AVX512VPOPCNTDQ ) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+
+} // namespace icelake
+} // namespace simdutf
+
+#endif // SIMDUTF_ICELAKE_IMPLEMENTATION_H
+/* end file src/simdutf/icelake/implementation.h */
+
+//
+// The rest need to be inside the region
+//
+/* begin file src/simdutf/icelake/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "icelake"
+// #define SIMDUTF_IMPLEMENTATION icelake
+
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+// nothing needed.
+#else
+SIMDUTF_TARGET_ICELAKE
+#endif
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized)
+#endif // end of workaround
+/* end file src/simdutf/icelake/begin.h */
+// Declarations
+/* begin file src/simdutf/icelake/bitmanipulation.h */
+#ifndef SIMDUTF_ICELAKE_BITMANIPULATION_H
+#define SIMDUTF_ICELAKE_BITMANIPULATION_H
+
+namespace simdutf {
+namespace icelake {
+namespace {
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) {
+ // note: we do not support legacy 32-bit Windows
+ return __popcnt64(input_num);// Visual Studio wants two underscores
+}
+#else
+simdutf_really_inline long long int count_ones(uint64_t input_num) {
+ return _popcnt64(input_num);
+}
+#endif
+
+#if SIMDUTF_NEED_TRAILING_ZEROES
+simdutf_really_inline int trailing_zeroes(uint64_t input_num) {
+#if SIMDUTF_REGULAR_VISUAL_STUDIO
+ return (int)_tzcnt_u64(input_num);
+#else // SIMDUTF_REGULAR_VISUAL_STUDIO
+ return __builtin_ctzll(input_num);
+#endif // SIMDUTF_REGULAR_VISUAL_STUDIO
+}
+#endif
+
+} // unnamed namespace
+} // namespace icelake
+} // namespace simdutf
+
+#endif // SIMDUTF_ICELAKE_BITMANIPULATION_H
+/* end file src/simdutf/icelake/bitmanipulation.h */
+/* begin file src/simdutf/icelake/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif // end of workaround
+/* end file src/simdutf/icelake/end.h */
+
+
+
+#endif // SIMDUTF_IMPLEMENTATION_ICELAKE
+#endif // SIMDUTF_ICELAKE_H
+/* end file src/simdutf/icelake.h */
+/* begin file src/simdutf/haswell.h */
+#ifndef SIMDUTF_HASWELL_H
+#define SIMDUTF_HASWELL_H
+
+#ifdef SIMDUTF_WESTMERE_H
+#error "haswell.h must be included before westmere.h"
+#endif
+#ifdef SIMDUTF_FALLBACK_H
+#error "haswell.h must be included before fallback.h"
+#endif
+
+
+// Default Haswell to on if this is x86-64. Even if we're not compiled for it, it could be selected
+// at runtime.
+#ifndef SIMDUTF_IMPLEMENTATION_HASWELL
+//
+// You do not want to restrict it like so: SIMDUTF_IS_X86_64 && __AVX2__
+// because we want to rely on *runtime dispatch*.
+//
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+#define SIMDUTF_IMPLEMENTATION_HASWELL 0
+#else
+#define SIMDUTF_IMPLEMENTATION_HASWELL (SIMDUTF_IS_X86_64)
+#endif
+
+#endif
+// To see why (__BMI__) && (__LZCNT__) are not part of this next line, see
+// https://github.com/simdutf/simdutf/issues/1247
+#if ((SIMDUTF_IMPLEMENTATION_HASWELL) && (SIMDUTF_IS_X86_64) && (__AVX2__))
+#define SIMDUTF_CAN_ALWAYS_RUN_HASWELL 1
+#else
+#define SIMDUTF_CAN_ALWAYS_RUN_HASWELL 0
+#endif
+
+#if SIMDUTF_IMPLEMENTATION_HASWELL
+
+#define SIMDUTF_TARGET_HASWELL SIMDUTF_TARGET_REGION("avx2,bmi,lzcnt,popcnt")
+
+namespace simdutf {
+/**
+ * Implementation for Haswell (Intel AVX2).
+ */
+namespace haswell {
+} // namespace haswell
+} // namespace simdutf
+
+//
+// These two need to be included outside SIMDUTF_TARGET_REGION
+//
+/* begin file src/simdutf/haswell/implementation.h */
+#ifndef SIMDUTF_HASWELL_IMPLEMENTATION_H
+#define SIMDUTF_HASWELL_IMPLEMENTATION_H
+
+
+// The constructor may be executed on any host, so we take care not to use SIMDUTF_TARGET_REGION
+namespace simdutf {
+namespace haswell {
+
+using namespace simdutf;
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation() : simdutf::implementation(
+ "haswell",
+ "Intel/AMD AVX2",
+ internal::instruction_set::AVX2 | internal::instruction_set::BMI1 | internal::instruction_set::BMI2
+ ) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused virtual result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused virtual result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused virtual size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+
+} // namespace haswell
+} // namespace simdutf
+
+#endif // SIMDUTF_HASWELL_IMPLEMENTATION_H
+/* end file src/simdutf/haswell/implementation.h */
+/* begin file src/simdutf/haswell/intrinsics.h */
+#ifndef SIMDUTF_HASWELL_INTRINSICS_H
+#define SIMDUTF_HASWELL_INTRINSICS_H
+
+
+#ifdef SIMDUTF_VISUAL_STUDIO
+// under clang within visual studio, this will include <x86intrin.h>
+#include <intrin.h> // visual studio or clang
+#else
+
+#if SIMDUTF_GCC11ORMORE
+// We should not get warnings while including <x86intrin.h> yet we do
+// under some versions of GCC.
+// If the x86intrin.h header has uninitialized values that are problematic,
+// it is a GCC issue, we want to ignore these warnings.
+SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized)
+#endif
+
+#include <x86intrin.h> // elsewhere
+
+
+#if SIMDUTF_GCC11ORMORE
+// cancels the suppression of the -Wuninitialized
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif
+
+#endif // SIMDUTF_VISUAL_STUDIO
+
+#ifdef SIMDUTF_CLANG_VISUAL_STUDIO
+/**
+ * You are not supposed, normally, to include these
+ * headers directly. Instead you should either include intrin.h
+ * or x86intrin.h. However, when compiling with clang
+ * under Windows (i.e., when _MSC_VER is set), these headers
+ * only get included *if* the corresponding features are detected
+ * from macros:
+ * e.g., if __AVX2__ is set... in turn, we normally set these
+ * macros by compiling against the corresponding architecture
+ * (e.g., arch:AVX2, -mavx2, etc.) which compiles the whole
+ * software with these advanced instructions. In simdutf, we
+ * want to compile the whole program for a generic target,
+ * and only target our specific kernels. As a workaround,
+ * we directly include the needed headers. These headers would
+ * normally guard against such usage, but we carefully included
+ * <x86intrin.h> (or <intrin.h>) before, so the headers
+ * are fooled.
+ */
+#include <bmiintrin.h> // for _blsr_u64
+#include <lzcntintrin.h> // for __lzcnt64
+#include <immintrin.h> // for most things (AVX2, AVX512, _popcnt64)
+#include <smmintrin.h>
+#include <tmmintrin.h>
+#include <avxintrin.h>
+#include <avx2intrin.h>
+// unfortunately, we may not get _blsr_u64, but, thankfully, clang
+// has it as a macro.
+#ifndef _blsr_u64
+// we roll our own
+#define _blsr_u64(n) ((n - 1) & n)
+#endif // _blsr_u64
+#endif // SIMDUTF_CLANG_VISUAL_STUDIO
+
+#endif // SIMDUTF_HASWELL_INTRINSICS_H
+/* end file src/simdutf/haswell/intrinsics.h */
+
+//
+// The rest need to be inside the region
+//
+/* begin file src/simdutf/haswell/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "haswell"
+// #define SIMDUTF_IMPLEMENTATION haswell
+
+#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL
+// nothing needed.
+#else
+SIMDUTF_TARGET_HASWELL
+#endif
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized)
+#endif // end of workaround
+/* end file src/simdutf/haswell/begin.h */
+// Declarations
+/* begin file src/simdutf/haswell/bitmanipulation.h */
+#ifndef SIMDUTF_HASWELL_BITMANIPULATION_H
+#define SIMDUTF_HASWELL_BITMANIPULATION_H
+
+namespace simdutf {
+namespace haswell {
+namespace {
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) {
+ // note: we do not support legacy 32-bit Windows
+ return __popcnt64(input_num);// Visual Studio wants two underscores
+}
+#else
+simdutf_really_inline long long int count_ones(uint64_t input_num) {
+ return _popcnt64(input_num);
+}
+#endif
+
+#if SIMDUTF_NEED_TRAILING_ZEROES
+simdutf_inline int trailing_zeroes(uint64_t input_num) {
+#if SIMDUTF_REGULAR_VISUAL_STUDIO
+ return (int)_tzcnt_u64(input_num);
+#else // SIMDUTF_REGULAR_VISUAL_STUDIO
+ return __builtin_ctzll(input_num);
+#endif // SIMDUTF_REGULAR_VISUAL_STUDIO
+}
+#endif
+
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+
+#endif // SIMDUTF_HASWELL_BITMANIPULATION_H
+/* end file src/simdutf/haswell/bitmanipulation.h */
+/* begin file src/simdutf/haswell/simd.h */
+#ifndef SIMDUTF_HASWELL_SIMD_H
+#define SIMDUTF_HASWELL_SIMD_H
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace simd {
+
+ // Forward-declared so they can be used by splat and friends.
+ template<typename Child>
+ struct base {
+ __m256i value;
+
+ // Zero constructor
+ simdutf_really_inline base() : value{__m256i()} {}
+
+ // Conversion from SIMD register
+ simdutf_really_inline base(const __m256i _value) : value(_value) {}
+ // Conversion to SIMD register
+ simdutf_really_inline operator const __m256i&() const { return this->value; }
+ simdutf_really_inline operator __m256i&() { return this->value; }
+ template <endianness big_endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ __m256i first = _mm256_cvtepu8_epi16(_mm256_castsi256_si128(*this));
+ __m256i second = _mm256_cvtepu8_epi16(_mm256_extractf128_si256(*this,1));
+ if (big_endian) {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ first = _mm256_shuffle_epi8(first, swap);
+ second = _mm256_shuffle_epi8(second, swap);
+ }
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), first);
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), second);
+ }
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const {
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr), _mm256_cvtepu8_epi32(_mm256_castsi256_si128(*this)));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr+8), _mm256_cvtepu8_epi32(_mm256_castsi256_si128(_mm256_srli_si256(*this,8))));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 16), _mm256_cvtepu8_epi32(_mm256_extractf128_si256(*this,1)));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(ptr + 24), _mm256_cvtepu8_epi32(_mm_srli_si128(_mm256_extractf128_si256(*this,1),8)));
+ }
+ // Bit operations
+ simdutf_really_inline Child operator|(const Child other) const { return _mm256_or_si256(*this, other); }
+ simdutf_really_inline Child operator&(const Child other) const { return _mm256_and_si256(*this, other); }
+ simdutf_really_inline Child operator^(const Child other) const { return _mm256_xor_si256(*this, other); }
+ simdutf_really_inline Child bit_andnot(const Child other) const { return _mm256_andnot_si256(other, *this); }
+ simdutf_really_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
+ simdutf_really_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
+ simdutf_really_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
+ };
+
+ // Forward-declared so they can be used by splat and friends.
+ template<typename T>
+ struct simd8;
+
+ template<typename T, typename Mask=simd8<bool>>
+ struct base8: base<simd8<T>> {
+ typedef uint32_t bitmask_t;
+ typedef uint64_t bitmask2_t;
+
+ simdutf_really_inline base8() : base<simd8<T>>() {}
+ simdutf_really_inline base8(const __m256i _value) : base<simd8<T>>(_value) {}
+ simdutf_really_inline T first() const { return _mm256_extract_epi8(*this,0); }
+ simdutf_really_inline T last() const { return _mm256_extract_epi8(*this,31); }
+ friend simdutf_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm256_cmpeq_epi8(lhs, rhs); }
+
+ static const int SIZE = sizeof(base<T>::value);
+
+ template<int N=1>
+ simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
+ return _mm256_alignr_epi8(*this, _mm256_permute2x128_si256(prev_chunk, *this, 0x21), 16 - N);
+ }
+ };
+
+ // SIMD byte mask type (returned by things like eq and gt)
+ template<>
+ struct simd8<bool>: base8<bool> {
+ static simdutf_really_inline simd8<bool> splat(bool _value) { return _mm256_set1_epi8(uint8_t(-(!!_value))); }
+
+ simdutf_really_inline simd8() : base8() {}
+ simdutf_really_inline simd8(const __m256i _value) : base8<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
+
+ simdutf_really_inline uint32_t to_bitmask() const { return uint32_t(_mm256_movemask_epi8(*this)); }
+ simdutf_really_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
+ simdutf_really_inline bool none() const { return _mm256_testz_si256(*this, *this); }
+ simdutf_really_inline bool all() const { return static_cast<uint32_t>(_mm256_movemask_epi8(*this)) == 0xFFFFFFFF; }
+ simdutf_really_inline simd8<bool> operator~() const { return *this ^ true; }
+ };
+
+ template<typename T>
+ struct base8_numeric: base8<T> {
+ static simdutf_really_inline simd8<T> splat(T _value) { return _mm256_set1_epi8(_value); }
+ static simdutf_really_inline simd8<T> zero() { return _mm256_setzero_si256(); }
+ static simdutf_really_inline simd8<T> load(const T values[32]) {
+ return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
+ }
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ static simdutf_really_inline simd8<T> repeat_16(
+ T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
+ T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
+ ) {
+ return simd8<T>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15,
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+ simdutf_really_inline base8_numeric() : base8<T>() {}
+ simdutf_really_inline base8_numeric(const __m256i _value) : base8<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[32]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd8<T> operator+(const simd8<T> other) const { return _mm256_add_epi8(*this, other); }
+ simdutf_really_inline simd8<T> operator-(const simd8<T> other) const { return _mm256_sub_epi8(*this, other); }
+ simdutf_really_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
+ simdutf_really_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
+
+ // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
+ return _mm256_shuffle_epi8(lookup_table, *this);
+ }
+
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(
+ L replace0, L replace1, L replace2, L replace3,
+ L replace4, L replace5, L replace6, L replace7,
+ L replace8, L replace9, L replace10, L replace11,
+ L replace12, L replace13, L replace14, L replace15) const {
+ return lookup_16(simd8<L>::repeat_16(
+ replace0, replace1, replace2, replace3,
+ replace4, replace5, replace6, replace7,
+ replace8, replace9, replace10, replace11,
+ replace12, replace13, replace14, replace15
+ ));
+ }
+ };
+
+
+ // Signed bytes
+ template<>
+ struct simd8<int8_t> : base8_numeric<int8_t> {
+ simdutf_really_inline simd8() : base8_numeric<int8_t>() {}
+ simdutf_really_inline simd8(const __m256i _value) : base8_numeric<int8_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const int8_t values[32]) : simd8(load(values)) {}
+ simdutf_really_inline operator simd8<uint8_t>() const;
+ // Member-by-member initialization
+ simdutf_really_inline simd8(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15,
+ int8_t v16, int8_t v17, int8_t v18, int8_t v19, int8_t v20, int8_t v21, int8_t v22, int8_t v23,
+ int8_t v24, int8_t v25, int8_t v26, int8_t v27, int8_t v28, int8_t v29, int8_t v30, int8_t v31
+ ) : simd8(_mm256_setr_epi8(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15,
+ v16,v17,v18,v19,v20,v21,v22,v23,
+ v24,v25,v26,v27,v28,v29,v30,v31
+ )) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<int8_t> repeat_16(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) {
+ return simd8<int8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15,
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+ simdutf_really_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; }
+ // Order-sensitive comparisons
+ simdutf_really_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm256_max_epi8(*this, other); }
+ simdutf_really_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm256_min_epi8(*this, other); }
+ simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(*this, other); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm256_cmpgt_epi8(other, *this); }
+ };
+
+ // Unsigned bytes
+ template<>
+ struct simd8<uint8_t>: base8_numeric<uint8_t> {
+ simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
+ simdutf_really_inline simd8(const __m256i _value) : base8_numeric<uint8_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const uint8_t values[32]) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd8(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15,
+ uint8_t v16, uint8_t v17, uint8_t v18, uint8_t v19, uint8_t v20, uint8_t v21, uint8_t v22, uint8_t v23,
+ uint8_t v24, uint8_t v25, uint8_t v26, uint8_t v27, uint8_t v28, uint8_t v29, uint8_t v30, uint8_t v31
+ ) : simd8(_mm256_setr_epi8(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15,
+ v16,v17,v18,v19,v20,v21,v22,v23,
+ v24,v25,v26,v27,v28,v29,v30,v31
+ )) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<uint8_t> repeat_16(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) {
+ return simd8<uint8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15,
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+
+ // Saturated math
+ simdutf_really_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm256_adds_epu8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm256_subs_epu8(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm256_max_epu8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm256_min_epu8(other, *this); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->lt_bits(other).any_bits_set(); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
+ simdutf_really_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
+ simdutf_really_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
+ simdutf_really_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
+ simdutf_really_inline bool is_ascii() const { return _mm256_movemask_epi8(*this) == 0; }
+ simdutf_really_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); }
+ simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
+ simdutf_really_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm256_testz_si256(*this, bits); }
+ simdutf_really_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm256_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm256_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
+ // Get one of the bits and make a bitmask out of it.
+ // e.g. value.get_bit<7>() gets the high bit
+ template<int N>
+ simdutf_really_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 7-N)); }
+ };
+ simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const { return this->value; }
+
+
+ template<typename T>
+ struct simd8x64 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
+ static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
+ simd8<T> chunks[NUM_CHUNKS];
+
+ simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
+ simd8x64<T>& operator=(const simd8<T> other) = delete; // no assignment allowed
+ simd8x64() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1) : chunks{chunk0, chunk1} {}
+ simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+sizeof(simd8<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd8<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd8<T>)*1/sizeof(T));
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+ uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
+ uint64_t r_hi = this->chunks[1].to_bitmask();
+ return r_lo | (r_hi << 32);
+ }
+
+ simdutf_really_inline simd8x64<T>& operator|=(const simd8x64<T> &other) {
+ this->chunks[0] |= other.chunks[0];
+ this->chunks[1] |= other.chunks[1];
+ return *this;
+ }
+
+ simdutf_really_inline simd8<T> reduce_or() const {
+ return this->chunks[0] | this->chunks[1];
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return this->reduce_or().is_ascii();
+ }
+
+ template <endianness endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*1);
+ }
+
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*1);
+ }
+
+ simdutf_really_inline simd8x64<T> bit_or(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<T>(
+ this->chunks[0] | mask,
+ this->chunks[1] | mask
+ );
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
+ return simd8x64<bool>(
+ this->chunks[0] == other.chunks[0],
+ this->chunks[1] == other.chunks[1]
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+
+ return simd8x64<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+ return simd8x64<bool>(
+ (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low),
+ (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t gt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] > mask,
+ this->chunks[1] > mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] >= mask,
+ this->chunks[1] >= mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
+ const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
+ return simd8x64<bool>(
+ (simd8<uint8_t>(__m256i(this->chunks[0])) >= mask),
+ (simd8<uint8_t>(__m256i(this->chunks[1])) >= mask)
+ ).to_bitmask();
+ }
+ }; // struct simd8x64<T>
+
+/* begin file src/simdutf/haswell/simd16-inl.h */
+#ifdef __GNUC__
+#if __GNUC__ < 8
+#define _mm256_set_m128i(xmm1, xmm2) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2)
+#define _mm256_setr_m128i(xmm2, xmm1) _mm256_permute2f128_si256(_mm256_castsi128_si256(xmm1), _mm256_castsi128_si256(xmm2), 2)
+#endif
+#endif
+
+template<typename T>
+struct simd16;
+
+template<typename T, typename Mask=simd16<bool>>
+struct base16: base<simd16<T>> {
+ using bitmask_type = uint32_t;
+
+ simdutf_really_inline base16() : base<simd16<T>>() {}
+ simdutf_really_inline base16(const __m256i _value) : base<simd16<T>>(_value) {}
+ template <typename Pointer>
+ simdutf_really_inline base16(const Pointer* ptr) : base16(_mm256_loadu_si256(reinterpret_cast<const __m256i*>(ptr))) {}
+ friend simdutf_really_inline Mask operator==(const simd16<T> lhs, const simd16<T> rhs) { return _mm256_cmpeq_epi16(lhs, rhs); }
+
+ /// the size of vector in bytes
+ static const int SIZE = sizeof(base<simd16<T>>::value);
+
+ /// the number of elements of type T a vector can hold
+ static const int ELEMENTS = SIZE / sizeof(T);
+
+ template<int N=1>
+ simdutf_really_inline simd16<T> prev(const simd16<T> prev_chunk) const {
+ return _mm256_alignr_epi8(*this, prev_chunk, 16 - N);
+ }
+};
+
+// SIMD byte mask type (returned by things like eq and gt)
+template<>
+struct simd16<bool>: base16<bool> {
+ static simdutf_really_inline simd16<bool> splat(bool _value) { return _mm256_set1_epi16(uint16_t(-(!!_value))); }
+
+ simdutf_really_inline simd16<bool>() : base16() {}
+ simdutf_really_inline simd16<bool>(const __m256i _value) : base16<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd16<bool>(bool _value) : base16<bool>(splat(_value)) {}
+
+ simdutf_really_inline bitmask_type to_bitmask() const { return _mm256_movemask_epi8(*this); }
+ simdutf_really_inline bool any() const { return !_mm256_testz_si256(*this, *this); }
+ simdutf_really_inline simd16<bool> operator~() const { return *this ^ true; }
+};
+
+template<typename T>
+struct base16_numeric: base16<T> {
+ static simdutf_really_inline simd16<T> splat(T _value) { return _mm256_set1_epi16(_value); }
+ static simdutf_really_inline simd16<T> zero() { return _mm256_setzero_si256(); }
+ static simdutf_really_inline simd16<T> load(const T values[8]) {
+ return _mm256_loadu_si256(reinterpret_cast<const __m256i *>(values));
+ }
+
+ simdutf_really_inline base16_numeric() : base16<T>() {}
+ simdutf_really_inline base16_numeric(const __m256i _value) : base16<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[8]) const { return _mm256_storeu_si256(reinterpret_cast<__m256i *>(dst), *this); }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd16<T> operator~() const { return *this ^ 0xFFFFu; }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd16<T> operator+(const simd16<T> other) const { return _mm256_add_epi16(*this, other); }
+ simdutf_really_inline simd16<T> operator-(const simd16<T> other) const { return _mm256_sub_epi16(*this, other); }
+ simdutf_really_inline simd16<T>& operator+=(const simd16<T> other) { *this = *this + other; return *static_cast<simd16<T>*>(this); }
+ simdutf_really_inline simd16<T>& operator-=(const simd16<T> other) { *this = *this - other; return *static_cast<simd16<T>*>(this); }
+};
+
+// Signed code units
+template<>
+struct simd16<int16_t> : base16_numeric<int16_t> {
+ simdutf_really_inline simd16() : base16_numeric<int16_t>() {}
+ simdutf_really_inline simd16(const __m256i _value) : base16_numeric<int16_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const int16_t*>(values))) {}
+ // Order-sensitive comparisons
+ simdutf_really_inline simd16<int16_t> max_val(const simd16<int16_t> other) const { return _mm256_max_epi16(*this, other); }
+ simdutf_really_inline simd16<int16_t> min_val(const simd16<int16_t> other) const { return _mm256_min_epi16(*this, other); }
+ simdutf_really_inline simd16<bool> operator>(const simd16<int16_t> other) const { return _mm256_cmpgt_epi16(*this, other); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<int16_t> other) const { return _mm256_cmpgt_epi16(other, *this); }
+};
+
+// Unsigned code units
+template<>
+struct simd16<uint16_t>: base16_numeric<uint16_t> {
+ simdutf_really_inline simd16() : base16_numeric<uint16_t>() {}
+ simdutf_really_inline simd16(const __m256i _value) : base16_numeric<uint16_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const uint16_t*>(values))) {}
+
+ // Saturated math
+ simdutf_really_inline simd16<uint16_t> saturating_add(const simd16<uint16_t> other) const { return _mm256_adds_epu16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> saturating_sub(const simd16<uint16_t> other) const { return _mm256_subs_epu16(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd16<uint16_t> max_val(const simd16<uint16_t> other) const { return _mm256_max_epu16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> min_val(const simd16<uint16_t> other) const { return _mm256_min_epu16(*this, other); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> gt_bits(const simd16<uint16_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> lt_bits(const simd16<uint16_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd16<bool> operator<=(const simd16<uint16_t> other) const { return other.max_val(*this) == other; }
+ simdutf_really_inline simd16<bool> operator>=(const simd16<uint16_t> other) const { return other.min_val(*this) == other; }
+ simdutf_really_inline simd16<bool> operator>(const simd16<uint16_t> other) const { return this->gt_bits(other).any_bits_set(); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<uint16_t> other) const { return this->gt_bits(other).any_bits_set(); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd16<bool> bits_not_set() const { return *this == uint16_t(0); }
+ simdutf_really_inline simd16<bool> bits_not_set(simd16<uint16_t> bits) const { return (*this & bits).bits_not_set(); }
+ simdutf_really_inline simd16<bool> any_bits_set() const { return ~this->bits_not_set(); }
+ simdutf_really_inline simd16<bool> any_bits_set(simd16<uint16_t> bits) const { return ~this->bits_not_set(bits); }
+
+ simdutf_really_inline bool bits_not_set_anywhere() const { return _mm256_testz_si256(*this, *this); }
+ simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
+ simdutf_really_inline bool bits_not_set_anywhere(simd16<uint16_t> bits) const { return _mm256_testz_si256(*this, bits); }
+ simdutf_really_inline bool any_bits_set_anywhere(simd16<uint16_t> bits) const { return !bits_not_set_anywhere(bits); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shr() const { return simd16<uint16_t>(_mm256_srli_epi16(*this, N)); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shl() const { return simd16<uint16_t>(_mm256_slli_epi16(*this, N)); }
+ // Get one of the bits and make a bitmask out of it.
+ // e.g. value.get_bit<7>() gets the high bit
+ template<int N>
+ simdutf_really_inline int get_bit() const { return _mm256_movemask_epi8(_mm256_slli_epi16(*this, 15-N)); }
+
+ // Change the endianness
+ simdutf_really_inline simd16<uint16_t> swap_bytes() const {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ return _mm256_shuffle_epi8(*this, swap);
+ }
+
+ // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector
+ static simdutf_really_inline simd8<uint8_t> pack(const simd16<uint16_t>& v0, const simd16<uint16_t>& v1) {
+ // Note: the AVX2 variant of pack operates on 128-bit lanes, thus
+ // we have to shuffle lanes in order to produce bytes in the
+ // correct order.
+
+ // get the 0th lanes
+ const __m128i lo_0 = _mm256_extracti128_si256(v0, 0);
+ const __m128i lo_1 = _mm256_extracti128_si256(v1, 0);
+
+ // get the 1st lanes
+ const __m128i hi_0 = _mm256_extracti128_si256(v0, 1);
+ const __m128i hi_1 = _mm256_extracti128_si256(v1, 1);
+
+ // build new vectors (shuffle lanes)
+ const __m256i t0 = _mm256_set_m128i(lo_1, lo_0);
+ const __m256i t1 = _mm256_set_m128i(hi_1, hi_0);
+
+ // pack code units in linear order from v0 and v1
+ return _mm256_packus_epi16(t0, t1);
+ }
+};
+
+
+ template<typename T>
+ struct simd16x32 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd16<T>);
+ static_assert(NUM_CHUNKS == 2, "Haswell kernel should use two registers per 64-byte block.");
+ simd16<T> chunks[NUM_CHUNKS];
+
+ simd16x32(const simd16x32<T>& o) = delete; // no copy allowed
+ simd16x32<T>& operator=(const simd16<T> other) = delete; // no assignment allowed
+ simd16x32() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd16x32(const simd16<T> chunk0, const simd16<T> chunk1) : chunks{chunk0, chunk1} {}
+ simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16<T>::load(ptr), simd16<T>::load(ptr+sizeof(simd16<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd16<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd16<T>)*1/sizeof(T));
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+ uint64_t r_lo = uint32_t(this->chunks[0].to_bitmask());
+ uint64_t r_hi = this->chunks[1].to_bitmask();
+ return r_lo | (r_hi << 32);
+ }
+
+ simdutf_really_inline simd16<T> reduce_or() const {
+ return this->chunks[0] | this->chunks[1];
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return this->reduce_or().is_ascii();
+ }
+
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*0);
+ this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16<T>));
+ }
+
+ simdutf_really_inline simd16x32<T> bit_or(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<T>(
+ this->chunks[0] | mask,
+ this->chunks[1] | mask
+ );
+ }
+
+ simdutf_really_inline void swap_bytes() {
+ this->chunks[0] = this->chunks[0].swap_bytes();
+ this->chunks[1] = this->chunks[1].swap_bytes();
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t eq(const simd16x32<uint16_t> &other) const {
+ return simd16x32<bool>(
+ this->chunks[0] == other.chunks[0],
+ this->chunks[1] == other.chunks[1]
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(low);
+ const simd16<T> mask_high = simd16<T>::splat(high);
+
+ return simd16x32<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(static_cast<T>(low-1));
+ const simd16<T> mask_high = simd16<T>::splat(static_cast<T>(high+1));
+ return simd16x32<bool>(
+ (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low),
+ (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask
+ ).to_bitmask();
+ }
+ }; // struct simd16x32<T>
+/* end file src/simdutf/haswell/simd16-inl.h */
+
+} // namespace simd
+
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+
+#endif // SIMDUTF_HASWELL_SIMD_H
+/* end file src/simdutf/haswell/simd.h */
+
+/* begin file src/simdutf/haswell/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif // end of workaround
+/* end file src/simdutf/haswell/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_HASWELL
+#endif // SIMDUTF_HASWELL_COMMON_H
+/* end file src/simdutf/haswell.h */
+/* begin file src/simdutf/westmere.h */
+#ifndef SIMDUTF_WESTMERE_H
+#define SIMDUTF_WESTMERE_H
+
+#ifdef SIMDUTF_FALLBACK_H
+#error "westmere.h must be included before fallback.h"
+#endif
+
+
+// Default Westmere to on if this is x86-64, unless we'll always select Haswell.
+#ifndef SIMDUTF_IMPLEMENTATION_WESTMERE
+//
+// You do not want to set it to (SIMDUTF_IS_X86_64 && !SIMDUTF_REQUIRES_HASWELL)
+// because you want to rely on runtime dispatch!
+//
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE || SIMDUTF_CAN_ALWAYS_RUN_HASWELL
+#define SIMDUTF_IMPLEMENTATION_WESTMERE 0
+#else
+#define SIMDUTF_IMPLEMENTATION_WESTMERE (SIMDUTF_IS_X86_64)
+#endif
+
+#endif
+
+#if (SIMDUTF_IMPLEMENTATION_WESTMERE && SIMDUTF_IS_X86_64 && __SSE4_2__)
+#define SIMDUTF_CAN_ALWAYS_RUN_WESTMERE 1
+#else
+#define SIMDUTF_CAN_ALWAYS_RUN_WESTMERE 0
+#endif
+
+#if SIMDUTF_IMPLEMENTATION_WESTMERE
+
+#define SIMDUTF_TARGET_WESTMERE SIMDUTF_TARGET_REGION("sse4.2,popcnt")
+
+namespace simdutf {
+/**
+ * Implementation for Westmere (Intel SSE4.2).
+ */
+namespace westmere {
+} // namespace westmere
+} // namespace simdutf
+
+//
+// These two need to be included outside SIMDUTF_TARGET_REGION
+//
+/* begin file src/simdutf/westmere/implementation.h */
+#ifndef SIMDUTF_WESTMERE_IMPLEMENTATION_H
+#define SIMDUTF_WESTMERE_IMPLEMENTATION_H
+
+
+// The constructor may be executed on any host, so we take care not to use SIMDUTF_TARGET_REGION
+namespace simdutf {
+namespace westmere {
+
+namespace {
+using namespace simdutf;
+}
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation() : simdutf::implementation("westmere", "Intel/AMD SSE4.2", internal::instruction_set::SSE42) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+
+} // namespace westmere
+} // namespace simdutf
+
+#endif // SIMDUTF_WESTMERE_IMPLEMENTATION_H
+/* end file src/simdutf/westmere/implementation.h */
+/* begin file src/simdutf/westmere/intrinsics.h */
+#ifndef SIMDUTF_WESTMERE_INTRINSICS_H
+#define SIMDUTF_WESTMERE_INTRINSICS_H
+
+#ifdef SIMDUTF_VISUAL_STUDIO
+// under clang within visual studio, this will include <x86intrin.h>
+#include <intrin.h> // visual studio or clang
+#else
+
+#if SIMDUTF_GCC11ORMORE
+// We should not get warnings while including <x86intrin.h> yet we do
+// under some versions of GCC.
+// If the x86intrin.h header has uninitialized values that are problematic,
+// it is a GCC issue, we want to ignore these warnings.
+SIMDUTF_DISABLE_GCC_WARNING(-Wuninitialized)
+#endif
+
+#include <x86intrin.h> // elsewhere
+
+
+#if SIMDUTF_GCC11ORMORE
+// cancels the suppression of the -Wuninitialized
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif
+
+#endif // SIMDUTF_VISUAL_STUDIO
+
+
+#ifdef SIMDUTF_CLANG_VISUAL_STUDIO
+/**
+ * You are not supposed, normally, to include these
+ * headers directly. Instead you should either include intrin.h
+ * or x86intrin.h. However, when compiling with clang
+ * under Windows (i.e., when _MSC_VER is set), these headers
+ * only get included *if* the corresponding features are detected
+ * from macros:
+ */
+#include <smmintrin.h> // for _mm_alignr_epi8
+#endif
+
+
+
+#endif // SIMDUTF_WESTMERE_INTRINSICS_H
+/* end file src/simdutf/westmere/intrinsics.h */
+
+//
+// The rest need to be inside the region
+//
+/* begin file src/simdutf/westmere/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "westmere"
+// #define SIMDUTF_IMPLEMENTATION westmere
+
+#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE
+// nothing needed.
+#else
+SIMDUTF_TARGET_WESTMERE
+#endif
+/* end file src/simdutf/westmere/begin.h */
+
+// Declarations
+/* begin file src/simdutf/westmere/bitmanipulation.h */
+#ifndef SIMDUTF_WESTMERE_BITMANIPULATION_H
+#define SIMDUTF_WESTMERE_BITMANIPULATION_H
+
+namespace simdutf {
+namespace westmere {
+namespace {
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+simdutf_really_inline unsigned __int64 count_ones(uint64_t input_num) {
+ // note: we do not support legacy 32-bit Windows
+ return __popcnt64(input_num);// Visual Studio wants two underscores
+}
+#else
+simdutf_really_inline long long int count_ones(uint64_t input_num) {
+ return _popcnt64(input_num);
+}
+#endif
+
+#if SIMDUTF_NEED_TRAILING_ZEROES
+simdutf_really_inline int trailing_zeroes(uint64_t input_num) {
+#if SIMDUTF_REGULAR_VISUAL_STUDIO
+ unsigned long ret;
+ _BitScanForward64(&ret, input_num);
+ return (int)ret;
+#else // SIMDUTF_REGULAR_VISUAL_STUDIO
+ return __builtin_ctzll(input_num);
+#endif // SIMDUTF_REGULAR_VISUAL_STUDIO
+}
+#endif
+
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+
+#endif // SIMDUTF_WESTMERE_BITMANIPULATION_H
+/* end file src/simdutf/westmere/bitmanipulation.h */
+/* begin file src/simdutf/westmere/simd.h */
+#ifndef SIMDUTF_WESTMERE_SIMD_H
+#define SIMDUTF_WESTMERE_SIMD_H
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace simd {
+
+ template<typename Child>
+ struct base {
+ __m128i value;
+
+ // Zero constructor
+ simdutf_really_inline base() : value{__m128i()} {}
+
+ // Conversion from SIMD register
+ simdutf_really_inline base(const __m128i _value) : value(_value) {}
+ // Conversion to SIMD register
+ simdutf_really_inline operator const __m128i&() const { return this->value; }
+ simdutf_really_inline operator __m128i&() { return this->value; }
+ template <endianness big_endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * p) const {
+ __m128i first = _mm_cvtepu8_epi16(*this);
+ __m128i second = _mm_cvtepu8_epi16(_mm_srli_si128(*this,8));
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ first = _mm_shuffle_epi8(first, swap);
+ second = _mm_shuffle_epi8(second, swap);
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p), first);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p+8), second);
+ }
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * p) const {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p), _mm_cvtepu8_epi32(*this));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p+4), _mm_cvtepu8_epi32(_mm_srli_si128(*this,4)));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p+8), _mm_cvtepu8_epi32(_mm_srli_si128(*this,8)));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(p+12), _mm_cvtepu8_epi32(_mm_srli_si128(*this,12)));
+ }
+ // Bit operations
+ simdutf_really_inline Child operator|(const Child other) const { return _mm_or_si128(*this, other); }
+ simdutf_really_inline Child operator&(const Child other) const { return _mm_and_si128(*this, other); }
+ simdutf_really_inline Child operator^(const Child other) const { return _mm_xor_si128(*this, other); }
+ simdutf_really_inline Child bit_andnot(const Child other) const { return _mm_andnot_si128(other, *this); }
+ simdutf_really_inline Child& operator|=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast | other; return *this_cast; }
+ simdutf_really_inline Child& operator&=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast & other; return *this_cast; }
+ simdutf_really_inline Child& operator^=(const Child other) { auto this_cast = static_cast<Child*>(this); *this_cast = *this_cast ^ other; return *this_cast; }
+ };
+
+ // Forward-declared so they can be used by splat and friends.
+ template<typename T>
+ struct simd8;
+
+ template<typename T, typename Mask=simd8<bool>>
+ struct base8: base<simd8<T>> {
+ typedef uint16_t bitmask_t;
+ typedef uint32_t bitmask2_t;
+
+ simdutf_really_inline T first() const { return _mm_extract_epi8(*this,0); }
+ simdutf_really_inline T last() const { return _mm_extract_epi8(*this,15); }
+ simdutf_really_inline base8() : base<simd8<T>>() {}
+ simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
+
+ friend simdutf_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) { return _mm_cmpeq_epi8(lhs, rhs); }
+
+ static const int SIZE = sizeof(base<simd8<T>>::value);
+
+ template<int N=1>
+ simdutf_really_inline simd8<T> prev(const simd8<T> prev_chunk) const {
+ return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
+ }
+ };
+
+ // SIMD byte mask type (returned by things like eq and gt)
+ template<>
+ struct simd8<bool>: base8<bool> {
+ static simdutf_really_inline simd8<bool> splat(bool _value) { return _mm_set1_epi8(uint8_t(-(!!_value))); }
+
+ simdutf_really_inline simd8() : base8() {}
+ simdutf_really_inline simd8(const __m128i _value) : base8<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(bool _value) : base8<bool>(splat(_value)) {}
+
+ simdutf_really_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
+ simdutf_really_inline bool any() const { return !_mm_testz_si128(*this, *this); }
+ simdutf_really_inline bool none() const { return _mm_testz_si128(*this, *this); }
+ simdutf_really_inline bool all() const { return _mm_movemask_epi8(*this) == 0xFFFF; }
+ simdutf_really_inline simd8<bool> operator~() const { return *this ^ true; }
+ };
+
+ template<typename T>
+ struct base8_numeric: base8<T> {
+ static simdutf_really_inline simd8<T> splat(T _value) { return _mm_set1_epi8(_value); }
+ static simdutf_really_inline simd8<T> zero() { return _mm_setzero_si128(); }
+ static simdutf_really_inline simd8<T> load(const T values[16]) {
+ return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
+ }
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ static simdutf_really_inline simd8<T> repeat_16(
+ T v0, T v1, T v2, T v3, T v4, T v5, T v6, T v7,
+ T v8, T v9, T v10, T v11, T v12, T v13, T v14, T v15
+ ) {
+ return simd8<T>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+ simdutf_really_inline base8_numeric() : base8<T>() {}
+ simdutf_really_inline base8_numeric(const __m128i _value) : base8<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[16]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd8<T> operator+(const simd8<T> other) const { return _mm_add_epi8(*this, other); }
+ simdutf_really_inline simd8<T> operator-(const simd8<T> other) const { return _mm_sub_epi8(*this, other); }
+ simdutf_really_inline simd8<T>& operator+=(const simd8<T> other) { *this = *this + other; return *static_cast<simd8<T>*>(this); }
+ simdutf_really_inline simd8<T>& operator-=(const simd8<T> other) { *this = *this - other; return *static_cast<simd8<T>*>(this); }
+
+ // Perform a lookup assuming the value is between 0 and 16 (undefined behavior for out of range values)
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
+ return _mm_shuffle_epi8(lookup_table, *this);
+ }
+
+ template<typename L>
+ simdutf_really_inline simd8<L> lookup_16(
+ L replace0, L replace1, L replace2, L replace3,
+ L replace4, L replace5, L replace6, L replace7,
+ L replace8, L replace9, L replace10, L replace11,
+ L replace12, L replace13, L replace14, L replace15) const {
+ return lookup_16(simd8<L>::repeat_16(
+ replace0, replace1, replace2, replace3,
+ replace4, replace5, replace6, replace7,
+ replace8, replace9, replace10, replace11,
+ replace12, replace13, replace14, replace15
+ ));
+ }
+ };
+
+ // Signed bytes
+ template<>
+ struct simd8<int8_t> : base8_numeric<int8_t> {
+ simdutf_really_inline simd8() : base8_numeric<int8_t>() {}
+ simdutf_really_inline simd8(const __m128i _value) : base8_numeric<int8_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const int8_t* values) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd8(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) : simd8(_mm_setr_epi8(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ )) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<int8_t> repeat_16(
+ int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11, int8_t v12, int8_t v13, int8_t v14, int8_t v15
+ ) {
+ return simd8<int8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+ simdutf_really_inline operator simd8<uint8_t>() const;
+ simdutf_really_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
+
+ // Order-sensitive comparisons
+ simdutf_really_inline simd8<int8_t> max_val(const simd8<int8_t> other) const { return _mm_max_epi8(*this, other); }
+ simdutf_really_inline simd8<int8_t> min_val(const simd8<int8_t> other) const { return _mm_min_epi8(*this, other); }
+ simdutf_really_inline simd8<bool> operator>(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(*this, other); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<int8_t> other) const { return _mm_cmpgt_epi8(other, *this); }
+ };
+
+ // Unsigned bytes
+ template<>
+ struct simd8<uint8_t>: base8_numeric<uint8_t> {
+ simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
+ simdutf_really_inline simd8(const __m128i _value) : base8_numeric<uint8_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const uint8_t* values) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd8(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) : simd8(_mm_setr_epi8(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ )) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<uint8_t> repeat_16(
+ uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5, uint8_t v6, uint8_t v7,
+ uint8_t v8, uint8_t v9, uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15
+ ) {
+ return simd8<uint8_t>(
+ v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10,v11,v12,v13,v14,v15
+ );
+ }
+
+ // Saturated math
+ simdutf_really_inline simd8<uint8_t> saturating_add(const simd8<uint8_t> other) const { return _mm_adds_epu8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> saturating_sub(const simd8<uint8_t> other) const { return _mm_subs_epu8(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd8<uint8_t> max_val(const simd8<uint8_t> other) const { return _mm_max_epu8(*this, other); }
+ simdutf_really_inline simd8<uint8_t> min_val(const simd8<uint8_t> other) const { return _mm_min_epu8(*this, other); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t> gt_bits(const simd8<uint8_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t> lt_bits(const simd8<uint8_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd8<bool> operator<=(const simd8<uint8_t> other) const { return other.max_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator>=(const simd8<uint8_t> other) const { return other.min_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator>(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
+ simdutf_really_inline simd8<bool> operator<(const simd8<uint8_t> other) const { return this->gt_bits(other).any_bits_set(); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd8<bool> bits_not_set() const { return *this == uint8_t(0); }
+ simdutf_really_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const { return (*this & bits).bits_not_set(); }
+ simdutf_really_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
+ simdutf_really_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const { return ~this->bits_not_set(bits); }
+ simdutf_really_inline bool is_ascii() const { return _mm_movemask_epi8(*this) == 0; }
+
+ simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
+ simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
+ simdutf_really_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const { return _mm_testz_si128(*this, bits); }
+ simdutf_really_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const { return !bits_not_set_anywhere(bits); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shr() const { return simd8<uint8_t>(_mm_srli_epi16(*this, N)) & uint8_t(0xFFu >> N); }
+ template<int N>
+ simdutf_really_inline simd8<uint8_t> shl() const { return simd8<uint8_t>(_mm_slli_epi16(*this, N)) & uint8_t(0xFFu << N); }
+ // Get one of the bits and make a bitmask out of it.
+ // e.g. value.get_bit<7>() gets the high bit
+ template<int N>
+ simdutf_really_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
+ };
+ simdutf_really_inline simd8<int8_t>::operator simd8<uint8_t>() const { return this->value; }
+
+ // Unsigned bytes
+ template<>
+ struct simd8<uint16_t>: base<uint16_t> {
+ static simdutf_really_inline simd8<uint16_t> splat(uint16_t _value) { return _mm_set1_epi16(_value); }
+ static simdutf_really_inline simd8<uint16_t> load(const uint16_t values[8]) {
+ return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
+ }
+
+ simdutf_really_inline simd8() : base<uint16_t>() {}
+ simdutf_really_inline simd8(const __m128i _value) : base<uint16_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(uint16_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const uint16_t* values) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd8(
+ uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7
+ ) : simd8(_mm_setr_epi16(
+ v0, v1, v2, v3, v4, v5, v6, v7
+ )) {}
+
+ // Saturated math
+ simdutf_really_inline simd8<uint16_t> saturating_add(const simd8<uint16_t> other) const { return _mm_adds_epu16(*this, other); }
+ simdutf_really_inline simd8<uint16_t> saturating_sub(const simd8<uint16_t> other) const { return _mm_subs_epu16(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd8<uint16_t> max_val(const simd8<uint16_t> other) const { return _mm_max_epu16(*this, other); }
+ simdutf_really_inline simd8<uint16_t> min_val(const simd8<uint16_t> other) const { return _mm_min_epu16(*this, other); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint16_t> gt_bits(const simd8<uint16_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint16_t> lt_bits(const simd8<uint16_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd8<bool> operator<=(const simd8<uint16_t> other) const { return other.max_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator>=(const simd8<uint16_t> other) const { return other.min_val(*this) == other; }
+ simdutf_really_inline simd8<bool> operator==(const simd8<uint16_t> other) const { return _mm_cmpeq_epi16(*this, other); }
+ simdutf_really_inline simd8<bool> operator&(const simd8<uint16_t> other) const { return _mm_and_si128(*this, other); }
+ simdutf_really_inline simd8<bool> operator|(const simd8<uint16_t> other) const { return _mm_or_si128(*this, other); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd8<bool> bits_not_set() const { return *this == uint16_t(0); }
+ simdutf_really_inline simd8<bool> any_bits_set() const { return ~this->bits_not_set(); }
+
+ simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
+ simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
+ simdutf_really_inline bool bits_not_set_anywhere(simd8<uint16_t> bits) const { return _mm_testz_si128(*this, bits); }
+ simdutf_really_inline bool any_bits_set_anywhere(simd8<uint16_t> bits) const { return !bits_not_set_anywhere(bits); }
+ };
+ template<typename T>
+ struct simd8x64 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
+ static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
+ simd8<T> chunks[NUM_CHUNKS];
+
+ simd8x64(const simd8x64<T>& o) = delete; // no copy allowed
+ simd8x64<T>& operator=(const simd8<T> other) = delete; // no assignment allowed
+ simd8x64() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1, const simd8<T> chunk2, const simd8<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
+ simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+2*sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+3*sizeof(simd8<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd8<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd8<T>)*1/sizeof(T));
+ this->chunks[2].store(ptr+sizeof(simd8<T>)*2/sizeof(T));
+ this->chunks[3].store(ptr+sizeof(simd8<T>)*3/sizeof(T));
+ }
+
+ simdutf_really_inline simd8x64<T>& operator |=(const simd8x64<T> &other) {
+ this->chunks[0] |= other.chunks[0];
+ this->chunks[1] |= other.chunks[1];
+ this->chunks[2] |= other.chunks[2];
+ this->chunks[3] |= other.chunks[3];
+ return *this;
+ }
+
+ simdutf_really_inline simd8<T> reduce_or() const {
+ return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return this->reduce_or().is_ascii();
+ }
+
+ template <endianness endian>
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*1);
+ this->chunks[2].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*2);
+ this->chunks[3].template store_ascii_as_utf16<endian>(ptr+sizeof(simd8<T>)*3);
+ }
+
+ simdutf_really_inline void store_ascii_as_utf32(char32_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*0);
+ this->chunks[1].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*1);
+ this->chunks[2].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*2);
+ this->chunks[3].store_ascii_as_utf32(ptr+sizeof(simd8<T>)*3);
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+ uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
+ uint64_t r1 = this->chunks[1].to_bitmask();
+ uint64_t r2 = this->chunks[2].to_bitmask();
+ uint64_t r3 = this->chunks[3].to_bitmask();
+ return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask,
+ this->chunks[2] == mask,
+ this->chunks[3] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
+ return simd8x64<bool>(
+ this->chunks[0] == other.chunks[0],
+ this->chunks[1] == other.chunks[1],
+ this->chunks[2] == other.chunks[2],
+ this->chunks[3] == other.chunks[3]
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask,
+ this->chunks[2] <= mask,
+ this->chunks[3] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+
+ return simd8x64<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low),
+ (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low),
+ (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low-1);
+ const simd8<T> mask_high = simd8<T>::splat(high+1);
+ return simd8x64<bool>(
+ (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low),
+ (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low),
+ (this->chunks[2] >= mask_high) | (this->chunks[2] <= mask_low),
+ (this->chunks[3] >= mask_high) | (this->chunks[3] <= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask,
+ this->chunks[2] < mask,
+ this->chunks[3] < mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t gt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] > mask,
+ this->chunks[1] > mask,
+ this->chunks[2] > mask,
+ this->chunks[3] > mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] >= mask,
+ this->chunks[1] >= mask,
+ this->chunks[2] >= mask,
+ this->chunks[3] >= mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
+ const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
+ return simd8x64<bool>(
+ simd8<uint8_t>(__m128i(this->chunks[0])) >= mask,
+ simd8<uint8_t>(__m128i(this->chunks[1])) >= mask,
+ simd8<uint8_t>(__m128i(this->chunks[2])) >= mask,
+ simd8<uint8_t>(__m128i(this->chunks[3])) >= mask
+ ).to_bitmask();
+ }
+ }; // struct simd8x64<T>
+
+/* begin file src/simdutf/westmere/simd16-inl.h */
+template<typename T>
+struct simd16;
+
+template<typename T, typename Mask=simd16<bool>>
+struct base16: base<simd16<T>> {
+ typedef uint16_t bitmask_t;
+ typedef uint32_t bitmask2_t;
+
+ simdutf_really_inline base16() : base<simd16<T>>() {}
+ simdutf_really_inline base16(const __m128i _value) : base<simd16<T>>(_value) {}
+ template <typename Pointer>
+ simdutf_really_inline base16(const Pointer* ptr) : base16(_mm_loadu_si128(reinterpret_cast<const __m128i*>(ptr))) {}
+
+ friend simdutf_really_inline Mask operator==(const simd16<T> lhs, const simd16<T> rhs) { return _mm_cmpeq_epi16(lhs, rhs); }
+
+ static const int SIZE = sizeof(base<simd16<T>>::value);
+
+ template<int N=1>
+ simdutf_really_inline simd16<T> prev(const simd16<T> prev_chunk) const {
+ return _mm_alignr_epi8(*this, prev_chunk, 16 - N);
+ }
+};
+
+// SIMD byte mask type (returned by things like eq and gt)
+template<>
+struct simd16<bool>: base16<bool> {
+ static simdutf_really_inline simd16<bool> splat(bool _value) { return _mm_set1_epi16(uint16_t(-(!!_value))); }
+
+ simdutf_really_inline simd16<bool>() : base16() {}
+ simdutf_really_inline simd16<bool>(const __m128i _value) : base16<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd16<bool>(bool _value) : base16<bool>(splat(_value)) {}
+
+ simdutf_really_inline int to_bitmask() const { return _mm_movemask_epi8(*this); }
+ simdutf_really_inline bool any() const { return !_mm_testz_si128(*this, *this); }
+ simdutf_really_inline simd16<bool> operator~() const { return *this ^ true; }
+};
+
+template<typename T>
+struct base16_numeric: base16<T> {
+ static simdutf_really_inline simd16<T> splat(T _value) { return _mm_set1_epi16(_value); }
+ static simdutf_really_inline simd16<T> zero() { return _mm_setzero_si128(); }
+ static simdutf_really_inline simd16<T> load(const T values[8]) {
+ return _mm_loadu_si128(reinterpret_cast<const __m128i *>(values));
+ }
+
+ simdutf_really_inline base16_numeric() : base16<T>() {}
+ simdutf_really_inline base16_numeric(const __m128i _value) : base16<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[8]) const { return _mm_storeu_si128(reinterpret_cast<__m128i *>(dst), *this); }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd16<T> operator~() const { return *this ^ 0xFFu; }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd16<T> operator+(const simd16<T> other) const { return _mm_add_epi16(*this, other); }
+ simdutf_really_inline simd16<T> operator-(const simd16<T> other) const { return _mm_sub_epi16(*this, other); }
+ simdutf_really_inline simd16<T>& operator+=(const simd16<T> other) { *this = *this + other; return *static_cast<simd16<T>*>(this); }
+ simdutf_really_inline simd16<T>& operator-=(const simd16<T> other) { *this = *this - other; return *static_cast<simd16<T>*>(this); }
+};
+
+// Signed code units
+template<>
+struct simd16<int16_t> : base16_numeric<int16_t> {
+ simdutf_really_inline simd16() : base16_numeric<int16_t>() {}
+ simdutf_really_inline simd16(const __m128i _value) : base16_numeric<int16_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd16(int16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const int16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const int16_t*>(values))) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd16(
+ int16_t v0, int16_t v1, int16_t v2, int16_t v3, int16_t v4, int16_t v5, int16_t v6, int16_t v7)
+ : simd16(_mm_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7)) {}
+ simdutf_really_inline operator simd16<uint16_t>() const;
+
+ // Order-sensitive comparisons
+ simdutf_really_inline simd16<int16_t> max_val(const simd16<int16_t> other) const { return _mm_max_epi16(*this, other); }
+ simdutf_really_inline simd16<int16_t> min_val(const simd16<int16_t> other) const { return _mm_min_epi16(*this, other); }
+ simdutf_really_inline simd16<bool> operator>(const simd16<int16_t> other) const { return _mm_cmpgt_epi16(*this, other); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<int16_t> other) const { return _mm_cmpgt_epi16(other, *this); }
+};
+
+// Unsigned code units
+template<>
+struct simd16<uint16_t>: base16_numeric<uint16_t> {
+ simdutf_really_inline simd16() : base16_numeric<uint16_t>() {}
+ simdutf_really_inline simd16(const __m128i _value) : base16_numeric<uint16_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd16(uint16_t _value) : simd16(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd16(const uint16_t* values) : simd16(load(values)) {}
+ simdutf_really_inline simd16(const char16_t* values) : simd16(load(reinterpret_cast<const uint16_t*>(values))) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd16(
+ uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7)
+ : simd16(_mm_setr_epi16(v0, v1, v2, v3, v4, v5, v6, v7)) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd16<uint16_t> repeat_16(
+ uint16_t v0, uint16_t v1, uint16_t v2, uint16_t v3, uint16_t v4, uint16_t v5, uint16_t v6, uint16_t v7
+ ) {
+ return simd16<uint16_t>(v0, v1, v2, v3, v4, v5, v6, v7);
+ }
+
+ // Saturated math
+ simdutf_really_inline simd16<uint16_t> saturating_add(const simd16<uint16_t> other) const { return _mm_adds_epu16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> saturating_sub(const simd16<uint16_t> other) const { return _mm_subs_epu16(*this, other); }
+
+ // Order-specific operations
+ simdutf_really_inline simd16<uint16_t> max_val(const simd16<uint16_t> other) const { return _mm_max_epu16(*this, other); }
+ simdutf_really_inline simd16<uint16_t> min_val(const simd16<uint16_t> other) const { return _mm_min_epu16(*this, other); }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> gt_bits(const simd16<uint16_t> other) const { return this->saturating_sub(other); }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd16<uint16_t> lt_bits(const simd16<uint16_t> other) const { return other.saturating_sub(*this); }
+ simdutf_really_inline simd16<bool> operator<=(const simd16<uint16_t> other) const { return other.max_val(*this) == other; }
+ simdutf_really_inline simd16<bool> operator>=(const simd16<uint16_t> other) const { return other.min_val(*this) == other; }
+ simdutf_really_inline simd16<bool> operator>(const simd16<uint16_t> other) const { return this->gt_bits(other).any_bits_set(); }
+ simdutf_really_inline simd16<bool> operator<(const simd16<uint16_t> other) const { return this->gt_bits(other).any_bits_set(); }
+
+ // Bit-specific operations
+ simdutf_really_inline simd16<bool> bits_not_set() const { return *this == uint16_t(0); }
+ simdutf_really_inline simd16<bool> bits_not_set(simd16<uint16_t> bits) const { return (*this & bits).bits_not_set(); }
+ simdutf_really_inline simd16<bool> any_bits_set() const { return ~this->bits_not_set(); }
+ simdutf_really_inline simd16<bool> any_bits_set(simd16<uint16_t> bits) const { return ~this->bits_not_set(bits); }
+
+ simdutf_really_inline bool bits_not_set_anywhere() const { return _mm_testz_si128(*this, *this); }
+ simdutf_really_inline bool any_bits_set_anywhere() const { return !bits_not_set_anywhere(); }
+ simdutf_really_inline bool bits_not_set_anywhere(simd16<uint16_t> bits) const { return _mm_testz_si128(*this, bits); }
+ simdutf_really_inline bool any_bits_set_anywhere(simd16<uint16_t> bits) const { return !bits_not_set_anywhere(bits); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shr() const { return simd16<uint16_t>(_mm_srli_epi16(*this, N)); }
+ template<int N>
+ simdutf_really_inline simd16<uint16_t> shl() const { return simd16<uint16_t>(_mm_slli_epi16(*this, N)); }
+ // Get one of the bits and make a bitmask out of it.
+ // e.g. value.get_bit<7>() gets the high bit
+ template<int N>
+ simdutf_really_inline int get_bit() const { return _mm_movemask_epi8(_mm_slli_epi16(*this, 7-N)); }
+
+ // Change the endianness
+ simdutf_really_inline simd16<uint16_t> swap_bytes() const {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ return _mm_shuffle_epi8(*this, swap);
+ }
+
+ // Pack with the unsigned saturation of two uint16_t code units into single uint8_t vector
+ static simdutf_really_inline simd8<uint8_t> pack(const simd16<uint16_t>& v0, const simd16<uint16_t>& v1) {
+ return _mm_packus_epi16(v0, v1);
+ }
+};
+simdutf_really_inline simd16<int16_t>::operator simd16<uint16_t>() const { return this->value; }
+
+template<typename T>
+ struct simd16x32 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd16<T>);
+ static_assert(NUM_CHUNKS == 4, "Westmere kernel should use four registers per 64-byte block.");
+ simd16<T> chunks[NUM_CHUNKS];
+
+ simd16x32(const simd16x32<T>& o) = delete; // no copy allowed
+ simd16x32<T>& operator=(const simd16<T> other) = delete; // no assignment allowed
+ simd16x32() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd16x32(const simd16<T> chunk0, const simd16<T> chunk1, const simd16<T> chunk2, const simd16<T> chunk3) : chunks{chunk0, chunk1, chunk2, chunk3} {}
+ simdutf_really_inline simd16x32(const T* ptr) : chunks{simd16<T>::load(ptr), simd16<T>::load(ptr+sizeof(simd16<T>)/sizeof(T)), simd16<T>::load(ptr+2*sizeof(simd16<T>)/sizeof(T)), simd16<T>::load(ptr+3*sizeof(simd16<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr+sizeof(simd16<T>)*0/sizeof(T));
+ this->chunks[1].store(ptr+sizeof(simd16<T>)*1/sizeof(T));
+ this->chunks[2].store(ptr+sizeof(simd16<T>)*2/sizeof(T));
+ this->chunks[3].store(ptr+sizeof(simd16<T>)*3/sizeof(T));
+ }
+
+ simdutf_really_inline simd16<T> reduce_or() const {
+ return (this->chunks[0] | this->chunks[1]) | (this->chunks[2] | this->chunks[3]);
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return this->reduce_or().is_ascii();
+ }
+
+ simdutf_really_inline void store_ascii_as_utf16(char16_t * ptr) const {
+ this->chunks[0].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*0);
+ this->chunks[1].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*1);
+ this->chunks[2].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*2);
+ this->chunks[3].store_ascii_as_utf16(ptr+sizeof(simd16<T>)*3);
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+ uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
+ uint64_t r1 = this->chunks[1].to_bitmask();
+ uint64_t r2 = this->chunks[2].to_bitmask();
+ uint64_t r3 = this->chunks[3].to_bitmask();
+ return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
+ }
+
+ simdutf_really_inline void swap_bytes() {
+ this->chunks[0] = this->chunks[0].swap_bytes();
+ this->chunks[1] = this->chunks[1].swap_bytes();
+ this->chunks[2] = this->chunks[2].swap_bytes();
+ this->chunks[3] = this->chunks[3].swap_bytes();
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] == mask,
+ this->chunks[1] == mask,
+ this->chunks[2] == mask,
+ this->chunks[3] == mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t eq(const simd16x32<uint16_t> &other) const {
+ return simd16x32<bool>(
+ this->chunks[0] == other.chunks[0],
+ this->chunks[1] == other.chunks[1],
+ this->chunks[2] == other.chunks[2],
+ this->chunks[3] == other.chunks[3]
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] <= mask,
+ this->chunks[1] <= mask,
+ this->chunks[2] <= mask,
+ this->chunks[3] <= mask
+ ).to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(low);
+ const simd16<T> mask_high = simd16<T>::splat(high);
+
+ return simd16x32<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low),
+ (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low),
+ (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd16<T> mask_low = simd16<T>::splat(static_cast<T>(low-1));
+ const simd16<T> mask_high = simd16<T>::splat(static_cast<T>(high+1));
+ return simd16x32<bool>(
+ (this->chunks[0] >= mask_high) | (this->chunks[0] <= mask_low),
+ (this->chunks[1] >= mask_high) | (this->chunks[1] <= mask_low),
+ (this->chunks[2] >= mask_high) | (this->chunks[2] <= mask_low),
+ (this->chunks[3] >= mask_high) | (this->chunks[3] <= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd16<T> mask = simd16<T>::splat(m);
+ return simd16x32<bool>(
+ this->chunks[0] < mask,
+ this->chunks[1] < mask,
+ this->chunks[2] < mask,
+ this->chunks[3] < mask
+ ).to_bitmask();
+ }
+ }; // struct simd16x32<T>
+/* end file src/simdutf/westmere/simd16-inl.h */
+
+} // namespace simd
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+
+#endif // SIMDUTF_WESTMERE_SIMD_INPUT_H
+/* end file src/simdutf/westmere/simd.h */
+
+/* begin file src/simdutf/westmere/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+/* end file src/simdutf/westmere/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_WESTMERE
+#endif // SIMDUTF_WESTMERE_COMMON_H
+/* end file src/simdutf/westmere.h */
+/* begin file src/simdutf/ppc64.h */
+#ifndef SIMDUTF_PPC64_H
+#define SIMDUTF_PPC64_H
+
+#ifdef SIMDUTF_FALLBACK_H
+#error "ppc64.h must be included before fallback.h"
+#endif
+
+
+#ifndef SIMDUTF_IMPLEMENTATION_PPC64
+#define SIMDUTF_IMPLEMENTATION_PPC64 (SIMDUTF_IS_PPC64)
+#endif
+#define SIMDUTF_CAN_ALWAYS_RUN_PPC64 SIMDUTF_IMPLEMENTATION_PPC64 && SIMDUTF_IS_PPC64
+
+
+
+#if SIMDUTF_IMPLEMENTATION_PPC64
+
+namespace simdutf {
+/**
+ * Implementation for ALTIVEC (PPC64).
+ */
+namespace ppc64 {
+} // namespace ppc64
+} // namespace simdutf
+
+/* begin file src/simdutf/ppc64/implementation.h */
+#ifndef SIMDUTF_PPC64_IMPLEMENTATION_H
+#define SIMDUTF_PPC64_IMPLEMENTATION_H
+
+
+namespace simdutf {
+namespace ppc64 {
+
+namespace {
+using namespace simdutf;
+} // namespace
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation()
+ : simdutf::implementation("ppc64", "PPC64 ALTIVEC",
+ internal::instruction_set::ALTIVEC) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+
+} // namespace ppc64
+} // namespace simdutf
+
+#endif // SIMDUTF_PPC64_IMPLEMENTATION_H
+/* end file src/simdutf/ppc64/implementation.h */
+
+/* begin file src/simdutf/ppc64/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "ppc64"
+// #define SIMDUTF_IMPLEMENTATION ppc64
+/* end file src/simdutf/ppc64/begin.h */
+
+// Declarations
+/* begin file src/simdutf/ppc64/intrinsics.h */
+#ifndef SIMDUTF_PPC64_INTRINSICS_H
+#define SIMDUTF_PPC64_INTRINSICS_H
+
+
+// This should be the correct header whether
+// you use visual studio or other compilers.
+#include <altivec.h>
+
+// These are defined by altivec.h in GCC toolchain, it is safe to undef them.
+#ifdef bool
+#undef bool
+#endif
+
+#ifdef vector
+#undef vector
+#endif
+
+#endif // SIMDUTF_PPC64_INTRINSICS_H
+/* end file src/simdutf/ppc64/intrinsics.h */
+/* begin file src/simdutf/ppc64/bitmanipulation.h */
+#ifndef SIMDUTF_PPC64_BITMANIPULATION_H
+#define SIMDUTF_PPC64_BITMANIPULATION_H
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+simdutf_really_inline int count_ones(uint64_t input_num) {
+ // note: we do not support legacy 32-bit Windows
+ return __popcnt64(input_num); // Visual Studio wants two underscores
+}
+#else
+simdutf_really_inline int count_ones(uint64_t input_num) {
+ return __builtin_popcountll(input_num);
+}
+#endif
+
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+
+#endif // SIMDUTF_PPC64_BITMANIPULATION_H
+/* end file src/simdutf/ppc64/bitmanipulation.h */
+/* begin file src/simdutf/ppc64/simd.h */
+#ifndef SIMDUTF_PPC64_SIMD_H
+#define SIMDUTF_PPC64_SIMD_H
+
+#include <type_traits>
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace simd {
+
+using __m128i = __vector unsigned char;
+
+template <typename Child> struct base {
+ __m128i value;
+
+ // Zero constructor
+ simdutf_really_inline base() : value{__m128i()} {}
+
+ // Conversion from SIMD register
+ simdutf_really_inline base(const __m128i _value) : value(_value) {}
+
+ // Conversion to SIMD register
+ simdutf_really_inline operator const __m128i &() const {
+ return this->value;
+ }
+ simdutf_really_inline operator __m128i &() { return this->value; }
+
+ // Bit operations
+ simdutf_really_inline Child operator|(const Child other) const {
+ return vec_or(this->value, (__m128i)other);
+ }
+ simdutf_really_inline Child operator&(const Child other) const {
+ return vec_and(this->value, (__m128i)other);
+ }
+ simdutf_really_inline Child operator^(const Child other) const {
+ return vec_xor(this->value, (__m128i)other);
+ }
+ simdutf_really_inline Child bit_andnot(const Child other) const {
+ return vec_andc(this->value, (__m128i)other);
+ }
+ simdutf_really_inline Child &operator|=(const Child other) {
+ auto this_cast = static_cast<Child*>(this);
+ *this_cast = *this_cast | other;
+ return *this_cast;
+ }
+ simdutf_really_inline Child &operator&=(const Child other) {
+ auto this_cast = static_cast<Child*>(this);
+ *this_cast = *this_cast & other;
+ return *this_cast;
+ }
+ simdutf_really_inline Child &operator^=(const Child other) {
+ auto this_cast = static_cast<Child*>(this);
+ *this_cast = *this_cast ^ other;
+ return *this_cast;
+ }
+};
+
+// Forward-declared so they can be used by splat and friends.
+template <typename T> struct simd8;
+
+template <typename T, typename Mask = simd8<bool>>
+struct base8 : base<simd8<T>> {
+ typedef uint16_t bitmask_t;
+ typedef uint32_t bitmask2_t;
+
+ simdutf_really_inline base8() : base<simd8<T>>() {}
+ simdutf_really_inline base8(const __m128i _value) : base<simd8<T>>(_value) {}
+
+ friend simdutf_really_inline Mask operator==(const simd8<T> lhs, const simd8<T> rhs) {
+ return (__m128i)vec_cmpeq(lhs.value, (__m128i)rhs);
+ }
+
+ static const int SIZE = sizeof(base<simd8<T>>::value);
+
+ template <int N = 1>
+ simdutf_really_inline simd8<T> prev(simd8<T> prev_chunk) const {
+ __m128i chunk = this->value;
+#ifdef __LITTLE_ENDIAN__
+ chunk = (__m128i)vec_reve(this->value);
+ prev_chunk = (__m128i)vec_reve((__m128i)prev_chunk);
+#endif
+ chunk = (__m128i)vec_sld((__m128i)prev_chunk, (__m128i)chunk, 16 - N);
+#ifdef __LITTLE_ENDIAN__
+ chunk = (__m128i)vec_reve((__m128i)chunk);
+#endif
+ return chunk;
+ }
+};
+
+// SIMD byte mask type (returned by things like eq and gt)
+template <> struct simd8<bool> : base8<bool> {
+ static simdutf_really_inline simd8<bool> splat(bool _value) {
+ return (__m128i)vec_splats((unsigned char)(-(!!_value)));
+ }
+
+ simdutf_really_inline simd8() : base8() {}
+ simdutf_really_inline simd8(const __m128i _value)
+ : base8<bool>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(bool _value)
+ : base8<bool>(splat(_value)) {}
+
+ simdutf_really_inline int to_bitmask() const {
+ __vector unsigned long long result;
+ const __m128i perm_mask = {0x78, 0x70, 0x68, 0x60, 0x58, 0x50, 0x48, 0x40,
+ 0x38, 0x30, 0x28, 0x20, 0x18, 0x10, 0x08, 0x00};
+
+ result = ((__vector unsigned long long)vec_vbpermq((__m128i)this->value,
+ (__m128i)perm_mask));
+#ifdef __LITTLE_ENDIAN__
+ return static_cast<int>(result[1]);
+#else
+ return static_cast<int>(result[0]);
+#endif
+ }
+ simdutf_really_inline bool any() const {
+ return !vec_all_eq(this->value, (__m128i)vec_splats(0));
+ }
+ simdutf_really_inline simd8<bool> operator~() const {
+ return this->value ^ (__m128i)splat(true);
+ }
+};
+
+template <typename T> struct base8_numeric : base8<T> {
+ static simdutf_really_inline simd8<T> splat(T value) {
+ (void)value;
+ return (__m128i)vec_splats(value);
+ }
+ static simdutf_really_inline simd8<T> zero() { return splat(0); }
+ static simdutf_really_inline simd8<T> load(const T values[16]) {
+ return (__m128i)(vec_vsx_ld(0, reinterpret_cast<const uint8_t *>(values)));
+ }
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ static simdutf_really_inline simd8<T> repeat_16(T v0, T v1, T v2, T v3, T v4,
+ T v5, T v6, T v7, T v8, T v9,
+ T v10, T v11, T v12, T v13,
+ T v14, T v15) {
+ return simd8<T>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13,
+ v14, v15);
+ }
+
+ simdutf_really_inline base8_numeric() : base8<T>() {}
+ simdutf_really_inline base8_numeric(const __m128i _value)
+ : base8<T>(_value) {}
+
+ // Store to array
+ simdutf_really_inline void store(T dst[16]) const {
+ vec_vsx_st(this->value, 0, reinterpret_cast<__m128i *>(dst));
+ }
+
+ // Override to distinguish from bool version
+ simdutf_really_inline simd8<T> operator~() const { return *this ^ 0xFFu; }
+
+ // Addition/subtraction are the same for signed and unsigned
+ simdutf_really_inline simd8<T> operator+(const simd8<T> other) const {
+ return (__m128i)((__m128i)this->value + (__m128i)other);
+ }
+ simdutf_really_inline simd8<T> operator-(const simd8<T> other) const {
+ return (__m128i)((__m128i)this->value - (__m128i)other);
+ }
+ simdutf_really_inline simd8<T> &operator+=(const simd8<T> other) {
+ *this = *this + other;
+ return *static_cast<simd8<T> *>(this);
+ }
+ simdutf_really_inline simd8<T> &operator-=(const simd8<T> other) {
+ *this = *this - other;
+ return *static_cast<simd8<T> *>(this);
+ }
+
+ // Perform a lookup assuming the value is between 0 and 16 (undefined behavior
+ // for out of range values)
+ template <typename L>
+ simdutf_really_inline simd8<L> lookup_16(simd8<L> lookup_table) const {
+ return (__m128i)vec_perm((__m128i)lookup_table, (__m128i)lookup_table, this->value);
+ }
+
+ template <typename L>
+ simdutf_really_inline simd8<L>
+ lookup_16(L replace0, L replace1, L replace2, L replace3, L replace4,
+ L replace5, L replace6, L replace7, L replace8, L replace9,
+ L replace10, L replace11, L replace12, L replace13, L replace14,
+ L replace15) const {
+ return lookup_16(simd8<L>::repeat_16(
+ replace0, replace1, replace2, replace3, replace4, replace5, replace6,
+ replace7, replace8, replace9, replace10, replace11, replace12,
+ replace13, replace14, replace15));
+ }
+};
+
+// Signed bytes
+template <> struct simd8<int8_t> : base8_numeric<int8_t> {
+ simdutf_really_inline simd8() : base8_numeric<int8_t>() {}
+ simdutf_really_inline simd8(const __m128i _value)
+ : base8_numeric<int8_t>(_value) {}
+
+ // Splat constructor
+ simdutf_really_inline simd8(int8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const int8_t *values) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline simd8(int8_t v0, int8_t v1, int8_t v2, int8_t v3,
+ int8_t v4, int8_t v5, int8_t v6, int8_t v7,
+ int8_t v8, int8_t v9, int8_t v10, int8_t v11,
+ int8_t v12, int8_t v13, int8_t v14, int8_t v15)
+ : simd8((__m128i)(__vector signed char){v0, v1, v2, v3, v4, v5, v6, v7,
+ v8, v9, v10, v11, v12, v13, v14,
+ v15}) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<int8_t>
+ repeat_16(int8_t v0, int8_t v1, int8_t v2, int8_t v3, int8_t v4, int8_t v5,
+ int8_t v6, int8_t v7, int8_t v8, int8_t v9, int8_t v10, int8_t v11,
+ int8_t v12, int8_t v13, int8_t v14, int8_t v15) {
+ return simd8<int8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15);
+ }
+
+ // Order-sensitive comparisons
+ simdutf_really_inline simd8<int8_t>
+ max_val(const simd8<int8_t> other) const {
+ return (__m128i)vec_max((__vector signed char)this->value,
+ (__vector signed char)(__m128i)other);
+ }
+ simdutf_really_inline simd8<int8_t>
+ min_val(const simd8<int8_t> other) const {
+ return (__m128i)vec_min((__vector signed char)this->value,
+ (__vector signed char)(__m128i)other);
+ }
+ simdutf_really_inline simd8<bool>
+ operator>(const simd8<int8_t> other) const {
+ return (__m128i)vec_cmpgt((__vector signed char)this->value,
+ (__vector signed char)(__m128i)other);
+ }
+ simdutf_really_inline simd8<bool>
+ operator<(const simd8<int8_t> other) const {
+ return (__m128i)vec_cmplt((__vector signed char)this->value,
+ (__vector signed char)(__m128i)other);
+ }
+};
+
+// Unsigned bytes
+template <> struct simd8<uint8_t> : base8_numeric<uint8_t> {
+ simdutf_really_inline simd8() : base8_numeric<uint8_t>() {}
+ simdutf_really_inline simd8(const __m128i _value)
+ : base8_numeric<uint8_t>(_value) {}
+ // Splat constructor
+ simdutf_really_inline simd8(uint8_t _value) : simd8(splat(_value)) {}
+ // Array constructor
+ simdutf_really_inline simd8(const uint8_t *values) : simd8(load(values)) {}
+ // Member-by-member initialization
+ simdutf_really_inline
+ simd8(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4, uint8_t v5,
+ uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9, uint8_t v10,
+ uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14, uint8_t v15)
+ : simd8((__m128i){v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15}) {}
+ // Repeat 16 values as many times as necessary (usually for lookup tables)
+ simdutf_really_inline static simd8<uint8_t>
+ repeat_16(uint8_t v0, uint8_t v1, uint8_t v2, uint8_t v3, uint8_t v4,
+ uint8_t v5, uint8_t v6, uint8_t v7, uint8_t v8, uint8_t v9,
+ uint8_t v10, uint8_t v11, uint8_t v12, uint8_t v13, uint8_t v14,
+ uint8_t v15) {
+ return simd8<uint8_t>(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12,
+ v13, v14, v15);
+ }
+
+ // Saturated math
+ simdutf_really_inline simd8<uint8_t>
+ saturating_add(const simd8<uint8_t> other) const {
+ return (__m128i)vec_adds(this->value, (__m128i)other);
+ }
+ simdutf_really_inline simd8<uint8_t>
+ saturating_sub(const simd8<uint8_t> other) const {
+ return (__m128i)vec_subs(this->value, (__m128i)other);
+ }
+
+ // Order-specific operations
+ simdutf_really_inline simd8<uint8_t>
+ max_val(const simd8<uint8_t> other) const {
+ return (__m128i)vec_max(this->value, (__m128i)other);
+ }
+ simdutf_really_inline simd8<uint8_t>
+ min_val(const simd8<uint8_t> other) const {
+ return (__m128i)vec_min(this->value, (__m128i)other);
+ }
+ // Same as >, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t>
+ gt_bits(const simd8<uint8_t> other) const {
+ return this->saturating_sub(other);
+ }
+ // Same as <, but only guarantees true is nonzero (< guarantees true = -1)
+ simdutf_really_inline simd8<uint8_t>
+ lt_bits(const simd8<uint8_t> other) const {
+ return other.saturating_sub(*this);
+ }
+ simdutf_really_inline simd8<bool>
+ operator<=(const simd8<uint8_t> other) const {
+ return other.max_val(*this) == other;
+ }
+ simdutf_really_inline simd8<bool>
+ operator>=(const simd8<uint8_t> other) const {
+ return other.min_val(*this) == other;
+ }
+ simdutf_really_inline simd8<bool>
+ operator>(const simd8<uint8_t> other) const {
+ return this->gt_bits(other).any_bits_set();
+ }
+ simdutf_really_inline simd8<bool>
+ operator<(const simd8<uint8_t> other) const {
+ return this->gt_bits(other).any_bits_set();
+ }
+
+ // Bit-specific operations
+ simdutf_really_inline simd8<bool> bits_not_set() const {
+ return (__m128i)vec_cmpeq(this->value, (__m128i)vec_splats(uint8_t(0)));
+ }
+ simdutf_really_inline simd8<bool> bits_not_set(simd8<uint8_t> bits) const {
+ return (*this & bits).bits_not_set();
+ }
+ simdutf_really_inline simd8<bool> any_bits_set() const {
+ return ~this->bits_not_set();
+ }
+ simdutf_really_inline simd8<bool> any_bits_set(simd8<uint8_t> bits) const {
+ return ~this->bits_not_set(bits);
+ }
+
+ simdutf_really_inline bool is_ascii() const {
+ return this->saturating_sub(0b01111111u).bits_not_set_anywhere();
+ }
+
+ simdutf_really_inline bool bits_not_set_anywhere() const {
+ return vec_all_eq(this->value, (__m128i)vec_splats(0));
+ }
+ simdutf_really_inline bool any_bits_set_anywhere() const {
+ return !bits_not_set_anywhere();
+ }
+ simdutf_really_inline bool bits_not_set_anywhere(simd8<uint8_t> bits) const {
+ return vec_all_eq(vec_and(this->value, (__m128i)bits),
+ (__m128i)vec_splats(0));
+ }
+ simdutf_really_inline bool any_bits_set_anywhere(simd8<uint8_t> bits) const {
+ return !bits_not_set_anywhere(bits);
+ }
+ template <int N> simdutf_really_inline simd8<uint8_t> shr() const {
+ return simd8<uint8_t>(
+ (__m128i)vec_sr(this->value, (__m128i)vec_splat_u8(N)));
+ }
+ template <int N> simdutf_really_inline simd8<uint8_t> shl() const {
+ return simd8<uint8_t>(
+ (__m128i)vec_sl(this->value, (__m128i)vec_splat_u8(N)));
+ }
+};
+
+template <typename T> struct simd8x64 {
+ static constexpr int NUM_CHUNKS = 64 / sizeof(simd8<T>);
+ static_assert(NUM_CHUNKS == 4,
+ "PPC64 kernel should use four registers per 64-byte block.");
+ simd8<T> chunks[NUM_CHUNKS];
+
+ simd8x64(const simd8x64<T> &o) = delete; // no copy allowed
+ simd8x64<T> &
+ operator=(const simd8<T> other) = delete; // no assignment allowed
+ simd8x64() = delete; // no default constructor allowed
+
+ simdutf_really_inline simd8x64(const simd8<T> chunk0, const simd8<T> chunk1,
+ const simd8<T> chunk2, const simd8<T> chunk3)
+ : chunks{chunk0, chunk1, chunk2, chunk3} {}
+
+ simdutf_really_inline simd8x64(const T* ptr) : chunks{simd8<T>::load(ptr), simd8<T>::load(ptr+sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+2*sizeof(simd8<T>)/sizeof(T)), simd8<T>::load(ptr+3*sizeof(simd8<T>)/sizeof(T))} {}
+
+ simdutf_really_inline void store(T* ptr) const {
+ this->chunks[0].store(ptr + sizeof(simd8<T>) * 0/sizeof(T));
+ this->chunks[1].store(ptr + sizeof(simd8<T>) * 1/sizeof(T));
+ this->chunks[2].store(ptr + sizeof(simd8<T>) * 2/sizeof(T));
+ this->chunks[3].store(ptr + sizeof(simd8<T>) * 3/sizeof(T));
+ }
+
+
+ simdutf_really_inline simd8x64<T>& operator |=(const simd8x64<T> &other) {
+ this->chunks[0] |= other.chunks[0];
+ this->chunks[1] |= other.chunks[1];
+ this->chunks[2] |= other.chunks[2];
+ this->chunks[3] |= other.chunks[3];
+ return *this;
+ }
+
+ simdutf_really_inline simd8<T> reduce_or() const {
+ return (this->chunks[0] | this->chunks[1]) |
+ (this->chunks[2] | this->chunks[3]);
+ }
+
+
+ simdutf_really_inline bool is_ascii() const {
+ return input.reduce_or().is_ascii();
+ }
+
+ simdutf_really_inline uint64_t to_bitmask() const {
+ uint64_t r0 = uint32_t(this->chunks[0].to_bitmask());
+ uint64_t r1 = this->chunks[1].to_bitmask();
+ uint64_t r2 = this->chunks[2].to_bitmask();
+ uint64_t r3 = this->chunks[3].to_bitmask();
+ return r0 | (r1 << 16) | (r2 << 32) | (r3 << 48);
+ }
+
+ simdutf_really_inline uint64_t eq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(this->chunks[0] == mask, this->chunks[1] == mask,
+ this->chunks[2] == mask, this->chunks[3] == mask)
+ .to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t eq(const simd8x64<uint8_t> &other) const {
+ return simd8x64<bool>(this->chunks[0] == other.chunks[0],
+ this->chunks[1] == other.chunks[1],
+ this->chunks[2] == other.chunks[2],
+ this->chunks[3] == other.chunks[3])
+ .to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t lteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(this->chunks[0] <= mask, this->chunks[1] <= mask,
+ this->chunks[2] <= mask, this->chunks[3] <= mask)
+ .to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+
+ return simd8x64<bool>(
+ (this->chunks[0] <= mask_high) & (this->chunks[0] >= mask_low),
+ (this->chunks[1] <= mask_high) & (this->chunks[1] >= mask_low),
+ (this->chunks[2] <= mask_high) & (this->chunks[2] >= mask_low),
+ (this->chunks[3] <= mask_high) & (this->chunks[3] >= mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t not_in_range(const T low, const T high) const {
+ const simd8<T> mask_low = simd8<T>::splat(low);
+ const simd8<T> mask_high = simd8<T>::splat(high);
+ return simd8x64<bool>(
+ (this->chunks[0] > mask_high) | (this->chunks[0] < mask_low),
+ (this->chunks[1] > mask_high) | (this->chunks[1] < mask_low),
+ (this->chunks[2] > mask_high) | (this->chunks[2] < mask_low),
+ (this->chunks[3] > mask_high) | (this->chunks[3] < mask_low)
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t lt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(this->chunks[0] < mask, this->chunks[1] < mask,
+ this->chunks[2] < mask, this->chunks[3] < mask)
+ .to_bitmask();
+ }
+
+ simdutf_really_inline uint64_t gt(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] > mask,
+ this->chunks[1] > mask,
+ this->chunks[2] > mask,
+ this->chunks[3] > mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq(const T m) const {
+ const simd8<T> mask = simd8<T>::splat(m);
+ return simd8x64<bool>(
+ this->chunks[0] >= mask,
+ this->chunks[1] >= mask,
+ this->chunks[2] >= mask,
+ this->chunks[3] >= mask
+ ).to_bitmask();
+ }
+ simdutf_really_inline uint64_t gteq_unsigned(const uint8_t m) const {
+ const simd8<uint8_t> mask = simd8<uint8_t>::splat(m);
+ return simd8x64<bool>(
+ simd8<uint8_t>(this->chunks[0]) >= mask,
+ simd8<uint8_t>(this->chunks[1]) >= mask,
+ simd8<uint8_t>(this->chunks[2]) >= mask,
+ simd8<uint8_t>(this->chunks[3]) >= mask
+ ).to_bitmask();
+ }
+}; // struct simd8x64<T>
+
+} // namespace simd
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+
+#endif // SIMDUTF_PPC64_SIMD_INPUT_H
+/* end file src/simdutf/ppc64/simd.h */
+
+/* begin file src/simdutf/ppc64/end.h */
+/* end file src/simdutf/ppc64/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_PPC64
+
+#endif // SIMDUTF_PPC64_H
+/* end file src/simdutf/ppc64.h */
+/* begin file src/simdutf/rvv.h */
+#ifndef SIMDUTF_RVV_H
+#define SIMDUTF_RVV_H
+
+#ifdef SIMDUTF_FALLBACK_H
+#error "rvv.h must be included before fallback.h"
+#endif
+
+
+#define SIMDUTF_CAN_ALWAYS_RUN_RVV SIMDUTF_IS_RVV
+
+#ifndef SIMDUTF_IMPLEMENTATION_RVV
+#define SIMDUTF_IMPLEMENTATION_RVV (SIMDUTF_CAN_ALWAYS_RUN_RVV || (SIMDUTF_IS_RISCV64 && SIMDUTF_HAS_RVV_INTRINSICS && SIMDUTF_HAS_RVV_TARGET_REGION))
+#endif
+
+#if SIMDUTF_IMPLEMENTATION_RVV
+
+#if SIMDUTF_CAN_ALWAYS_RUN_RVV
+#define SIMDUTF_TARGET_RVV
+#else
+#define SIMDUTF_TARGET_RVV SIMDUTF_TARGET_REGION("arch=+v")
+#endif
+#if !SIMDUTF_IS_ZVBB && SIMDUTF_HAS_ZVBB_INTRINSICS
+#define SIMDUTF_TARGET_ZVBB SIMDUTF_TARGET_REGION("arch=+v,+zvbb")
+#endif
+
+namespace simdutf {
+namespace rvv {
+} // namespace rvv
+} // namespace simdutf
+
+/* begin file src/simdutf/rvv/implementation.h */
+#ifndef SIMDUTF_RVV_IMPLEMENTATION_H
+#define SIMDUTF_RVV_IMPLEMENTATION_H
+
+
+namespace simdutf {
+namespace rvv {
+
+namespace {
+using namespace simdutf;
+} // namespace
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation()
+ : simdutf::implementation("rvv", "RISC-V Vector Extension",
+ internal::instruction_set::RVV)
+ , _supports_zvbb(internal::detect_supported_architectures() & internal::instruction_set::ZVBB)
+ {}
+ simdutf_warn_unused int detect_encodings(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char *buf, size_t len, char *utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char *buf, size_t len, char32_t *utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char *buf, size_t len, char *latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char *buf, size_t len, char *latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char *buf, size_t len, char16_t *utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char *buf, size_t len, char16_t *utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char *buf, size_t len, char16_t *utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char *buf, size_t len, char16_t *utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char *buf, size_t len, char32_t *utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char *buf, size_t len, char32_t *utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t *buf, size_t len, char *latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t *buf, size_t len, char *utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t *buf, size_t len, char *latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len, char *latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t *buf, size_t len, char *latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t *buf, size_t len, char16_t *utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t *buf, size_t len, char32_t *utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t *buf, size_t len, char16_t *output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t len) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t len) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char *buf, size_t len) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+private:
+ const bool _supports_zvbb;
+
+#if SIMDUTF_IS_ZVBB
+ bool supports_zvbb() const { return true; }
+#elif SIMDUTF_HAS_ZVBB_INTRINSICS
+ bool supports_zvbb() const { return _supports_zvbb; }
+#else
+ bool supports_zvbb() const { return false; }
+#endif
+};
+
+} // namespace rvv
+} // namespace simdutf
+
+#endif // SIMDUTF_RVV_IMPLEMENTATION_H
+/* end file src/simdutf/rvv/implementation.h */
+/* begin file src/simdutf/rvv/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "rvv"
+// #define SIMDUTF_IMPLEMENTATION rvv
+
+#if SIMDUTF_CAN_ALWAYS_RUN_RVV
+// nothing needed.
+#else
+SIMDUTF_TARGET_RVV
+#endif
+/* end file src/simdutf/rvv/begin.h */
+/* begin file src/simdutf/rvv/intrinsics.h */
+#ifndef SIMDUTF_RVV_INTRINSICS_H
+#define SIMDUTF_RVV_INTRINSICS_H
+
+
+#include <riscv_vector.h>
+
+#if __riscv_v_intrinsic >= 1000000 || __GCC__ >= 14
+#define simdutf_vrgather_u8m1x2(tbl, idx) __riscv_vcreate_v_u8m1_u8m2( \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), __riscv_vsetvlmax_e8m1()), \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), __riscv_vsetvlmax_e8m1()));
+
+#define simdutf_vrgather_u8m1x4(tbl, idx) __riscv_vcreate_v_u8m1_u8m4( \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), __riscv_vsetvlmax_e8m1()), \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), __riscv_vsetvlmax_e8m1()), \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), __riscv_vsetvlmax_e8m1()), \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), __riscv_vsetvlmax_e8m1()));
+#else
+// This has worse codegen on gcc
+#define simdutf_vrgather_u8m1x2(tbl, idx) \
+ __riscv_vset_v_u8m1_u8m2(__riscv_vlmul_ext_v_u8m1_u8m2( \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 0), __riscv_vsetvlmax_e8m1())), 1, \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m2_u8m1(idx, 1), __riscv_vsetvlmax_e8m1()))
+
+#define simdutf_vrgather_u8m1x4(tbl, idx) \
+ __riscv_vset_v_u8m1_u8m4(__riscv_vset_v_u8m1_u8m4(\
+ __riscv_vset_v_u8m1_u8m4(__riscv_vlmul_ext_v_u8m1_u8m4( \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 0), __riscv_vsetvlmax_e8m1())), 1, \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 1), __riscv_vsetvlmax_e8m1())), 2, \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 2), __riscv_vsetvlmax_e8m1())), 3, \
+ __riscv_vrgather_vv_u8m1(tbl, __riscv_vget_v_u8m4_u8m1(idx, 3), __riscv_vsetvlmax_e8m1()))
+#endif
+
+/* Zvbb adds dedicated support for endianness swaps with vrev8, but if we can't
+ * use that, we have to emulate it with the standard V extension.
+ * Using LMUL=1 vrgathers could be faster than the srl+macc variant, but that
+ * would increase register pressure, and vrgather implementations performance
+ * varies a lot. */
+enum class simdutf_ByteFlip { NONE, V, ZVBB };
+
+template<simdutf_ByteFlip method>
+simdutf_really_inline static uint16_t simdutf_byteflip(uint16_t v) {
+ if (method != simdutf_ByteFlip::NONE)
+ return (uint16_t)((v*1u) << 8 | (v*1u) >> 8);
+ return v;
+}
+
+#ifdef SIMDUTF_TARGET_ZVBB
+SIMDUTF_UNTARGET_REGION
+SIMDUTF_TARGET_ZVBB
+#endif
+
+template<simdutf_ByteFlip method>
+simdutf_really_inline static vuint16m1_t simdutf_byteflip(vuint16m1_t v, size_t vl) {
+#if SIMDUTF_HAS_ZVBB_INTRINSICS
+ if (method == simdutf_ByteFlip::ZVBB)
+ return __riscv_vrev8_v_u16m1(v, vl);
+#endif
+ if (method == simdutf_ByteFlip::V)
+ return __riscv_vmacc_vx_u16m1(__riscv_vsrl_vx_u16m1(v, 8, vl), 0x100, v, vl);
+ return v;
+}
+
+template<simdutf_ByteFlip method>
+simdutf_really_inline static vuint16m2_t simdutf_byteflip(vuint16m2_t v, size_t vl) {
+#if SIMDUTF_HAS_ZVBB_INTRINSICS
+ if (method == simdutf_ByteFlip::ZVBB)
+ return __riscv_vrev8_v_u16m2(v, vl);
+#endif
+ if (method == simdutf_ByteFlip::V)
+ return __riscv_vmacc_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 8, vl), 0x100, v, vl);
+ return v;
+}
+
+template<simdutf_ByteFlip method>
+simdutf_really_inline static vuint16m4_t simdutf_byteflip(vuint16m4_t v, size_t vl) {
+#if SIMDUTF_HAS_ZVBB_INTRINSICS
+ if (method == simdutf_ByteFlip::ZVBB)
+ return __riscv_vrev8_v_u16m4(v, vl);
+#endif
+ if (method == simdutf_ByteFlip::V)
+ return __riscv_vmacc_vx_u16m4(__riscv_vsrl_vx_u16m4(v, 8, vl), 0x100, v, vl);
+ return v;
+}
+
+template<simdutf_ByteFlip method>
+simdutf_really_inline static vuint16m8_t simdutf_byteflip(vuint16m8_t v, size_t vl) {
+#if SIMDUTF_HAS_ZVBB_INTRINSICS
+ if (method == simdutf_ByteFlip::ZVBB)
+ return __riscv_vrev8_v_u16m8(v, vl);
+#endif
+ if (method == simdutf_ByteFlip::V)
+ return __riscv_vmacc_vx_u16m8(__riscv_vsrl_vx_u16m8(v, 8, vl), 0x100, v, vl);
+ return v;
+}
+
+#ifdef SIMDUTF_TARGET_ZVBB
+SIMDUTF_UNTARGET_REGION
+SIMDUTF_TARGET_RVV
+#endif
+
+#endif // SIMDUTF_RVV_INTRINSICS_H
+/* end file src/simdutf/rvv/intrinsics.h */
+/* begin file src/simdutf/rvv/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_RVV
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+/* end file src/simdutf/rvv/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_RVV
+
+#endif // SIMDUTF_RVV_H
+/* end file src/simdutf/rvv.h */
+/* begin file src/simdutf/fallback.h */
+#ifndef SIMDUTF_FALLBACK_H
+#define SIMDUTF_FALLBACK_H
+
+
+// Note that fallback.h is always imported last.
+
+// Default Fallback to on unless a builtin implementation has already been selected.
+#ifndef SIMDUTF_IMPLEMENTATION_FALLBACK
+#if SIMDUTF_CAN_ALWAYS_RUN_ARM64 || SIMDUTF_CAN_ALWAYS_RUN_ICELAKE || SIMDUTF_CAN_ALWAYS_RUN_HASWELL || SIMDUTF_CAN_ALWAYS_RUN_WESTMERE || SIMDUTF_CAN_ALWAYS_RUN_PPC64 || SIMDUTF_CAN_ALWAYS_RUN_RVV
+#define SIMDUTF_IMPLEMENTATION_FALLBACK 0
+#else
+#define SIMDUTF_IMPLEMENTATION_FALLBACK 1
+#endif
+#endif
+
+#define SIMDUTF_CAN_ALWAYS_RUN_FALLBACK (SIMDUTF_IMPLEMENTATION_FALLBACK)
+
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+
+namespace simdutf {
+/**
+ * Fallback implementation (runs on any machine).
+ */
+namespace fallback {
+} // namespace fallback
+} // namespace simdutf
+
+/* begin file src/simdutf/fallback/implementation.h */
+#ifndef SIMDUTF_FALLBACK_IMPLEMENTATION_H
+#define SIMDUTF_FALLBACK_IMPLEMENTATION_H
+
+
+namespace simdutf {
+namespace fallback {
+
+namespace {
+using namespace simdutf;
+}
+
+class implementation final : public simdutf::implementation {
+public:
+ simdutf_really_inline implementation() : simdutf::implementation(
+ "fallback",
+ "Generic fallback implementation",
+ 0
+ ) {}
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept final;
+ simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) const noexcept final;
+ void change_endianness_utf16(const char16_t * buf, size_t length, char16_t * output) const noexcept final;
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) const noexcept;
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) const noexcept;
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept;
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept;
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept;
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept;
+};
+} // namespace fallback
+} // namespace simdutf
+
+#endif // SIMDUTF_FALLBACK_IMPLEMENTATION_H
+/* end file src/simdutf/fallback/implementation.h */
+
+/* begin file src/simdutf/fallback/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "fallback"
+// #define SIMDUTF_IMPLEMENTATION fallback
+/* end file src/simdutf/fallback/begin.h */
+
+// Declarations
+/* begin file src/simdutf/fallback/bitmanipulation.h */
+#ifndef SIMDUTF_FALLBACK_BITMANIPULATION_H
+#define SIMDUTF_FALLBACK_BITMANIPULATION_H
+
+#include <limits>
+
+namespace simdutf {
+namespace fallback {
+namespace {
+
+} // unnamed namespace
+} // namespace fallback
+} // namespace simdutf
+
+#endif // SIMDUTF_FALLBACK_BITMANIPULATION_H
+/* end file src/simdutf/fallback/bitmanipulation.h */
+
+/* begin file src/simdutf/fallback/end.h */
+/* end file src/simdutf/fallback/end.h */
+
+#endif // SIMDUTF_IMPLEMENTATION_FALLBACK
+#endif // SIMDUTF_FALLBACK_H
+/* end file src/simdutf/fallback.h */
+
+/* begin file src/scalar/utf8.h */
+#ifndef SIMDUTF_UTF8_H
+#define SIMDUTF_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8 {
+#if SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_RVV
+// only used by the fallback kernel.
+// credit: based on code from Google Fuchsia (Apache Licensed)
+inline simdutf_warn_unused bool validate(const char *buf, size_t len) noexcept {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ uint64_t pos = 0;
+ uint32_t code_point = 0;
+ while (pos < len) {
+ // check of the next 16 bytes are ascii.
+ uint64_t next_pos = pos + 16;
+ if (next_pos <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ std::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ pos = next_pos;
+ continue;
+ }
+ }
+ unsigned char byte = data[pos];
+
+ while (byte < 0b10000000) {
+ if (++pos == len) { return true; }
+ byte = data[pos];
+ }
+
+ if ((byte & 0b11100000) == 0b11000000) {
+ next_pos = pos + 2;
+ if (next_pos > len) { return false; }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; }
+ // range check
+ code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if ((code_point < 0x80) || (0x7ff < code_point)) { return false; }
+ } else if ((byte & 0b11110000) == 0b11100000) {
+ next_pos = pos + 3;
+ if (next_pos > len) { return false; }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return false; }
+ // range check
+ code_point = (byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if ((code_point < 0x800) || (0xffff < code_point) ||
+ (0xd7ff < code_point && code_point < 0xe000)) {
+ return false;
+ }
+ } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
+ next_pos = pos + 4;
+ if (next_pos > len) { return false; }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return false; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return false; }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return false; }
+ // range check
+ code_point =
+ (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff || 0x10ffff < code_point) { return false; }
+ } else {
+ // we may have a continuation
+ return false;
+ }
+ pos = next_pos;
+ }
+ return true;
+}
+#endif
+
+inline simdutf_warn_unused result validate_with_errors(const char *buf, size_t len) noexcept {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ uint32_t code_point = 0;
+ while (pos < len) {
+ // check of the next 16 bytes are ascii.
+ size_t next_pos = pos + 16;
+ if (next_pos <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ std::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ pos = next_pos;
+ continue;
+ }
+ }
+ unsigned char byte = data[pos];
+
+ while (byte < 0b10000000) {
+ if (++pos == len) { return result(error_code::SUCCESS, len); }
+ byte = data[pos];
+ }
+
+ if ((byte & 0b11100000) == 0b11000000) {
+ next_pos = pos + 2;
+ if (next_pos > len) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ code_point = (byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if ((code_point < 0x80) || (0x7ff < code_point)) { return result(error_code::OVERLONG, pos); }
+ } else if ((byte & 0b11110000) == 0b11100000) {
+ next_pos = pos + 3;
+ if (next_pos > len) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ code_point = (byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if ((code_point < 0x800) || (0xffff < code_point)) { return result(error_code::OVERLONG, pos);}
+ if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); }
+ } else if ((byte & 0b11111000) == 0b11110000) { // 0b11110000
+ next_pos = pos + 4;
+ if (next_pos > len) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ code_point =
+ (byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); }
+ if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); }
+ } else {
+ // we either have too many continuation bytes or an invalid leading byte
+ if ((byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); }
+ else { return result(error_code::HEADER_BITS, pos); }
+ }
+ pos = next_pos;
+ }
+ return result(error_code::SUCCESS, len);
+}
+
+// Finds the previous leading byte starting backward from buf and validates with errors from there
+// Used to pinpoint the location of an error when an invalid chunk is detected
+// We assume that the stream starts with a leading byte, and to check that it is the case, we
+// ask that you pass a pointer to the start of the stream (start).
+inline simdutf_warn_unused result rewind_and_validate_with_errors(const char *start, const char *buf, size_t len) noexcept {
+ // First check that we start with a leading byte
+ if ((*start & 0b11000000) == 0b10000000) {
+ return result(error_code::TOO_LONG, 0);
+ }
+ size_t extra_len{0};
+ // A leading byte cannot be further than 4 bytes away
+ for(int i = 0; i < 5; i++) {
+ unsigned char byte = *buf;
+ if ((byte & 0b11000000) != 0b10000000) {
+ break;
+ } else {
+ buf--;
+ extra_len++;
+ }
+ }
+
+ result res = validate_with_errors(buf, len + extra_len);
+ res.count -= extra_len;
+ return res;
+}
+
+inline size_t count_code_points(const char* buf, size_t len) {
+ const int8_t * p = reinterpret_cast<const int8_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ // -65 is 0b10111111, anything larger in two-complement's should start a new code point.
+ if(p[i] > -65) { counter++; }
+ }
+ return counter;
+}
+
+inline size_t utf16_length_from_utf8(const char* buf, size_t len) {
+ const int8_t * p = reinterpret_cast<const int8_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ if(p[i] > -65) { counter++; }
+ if(uint8_t(p[i]) >= 240) { counter++; }
+ }
+ return counter;
+}
+
+simdutf_warn_unused inline size_t trim_partial_utf8(const char *input, size_t length) {
+ if (length < 3) {
+ switch (length) {
+ case 2:
+ if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left
+ if (uint8_t(input[length-2]) >= 0xe0) { return length-2; } // 3- and 4-byte characters with only 2 bytes left
+ return length;
+ case 1:
+ if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left
+ return length;
+ case 0:
+ return length;
+ }
+ }
+ if (uint8_t(input[length-1]) >= 0xc0) { return length-1; } // 2-, 3- and 4-byte characters with only 1 byte left
+ if (uint8_t(input[length-2]) >= 0xe0) { return length-2; } // 3- and 4-byte characters with only 1 byte left
+ if (uint8_t(input[length-3]) >= 0xf0) { return length-3; } // 4-byte characters with only 3 bytes left
+ return length;
+}
+
+} // utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8.h */
+/* begin file src/scalar/utf16.h */
+#ifndef SIMDUTF_UTF16_H
+#define SIMDUTF_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16 {
+
+inline simdutf_warn_unused uint16_t swap_bytes(const uint16_t word) {
+ return uint16_t((word >> 8) | (word << 8));
+}
+
+template <endianness big_endian>
+inline simdutf_warn_unused bool validate(const char16_t *buf, size_t len) noexcept {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ uint64_t pos = 0;
+ while (pos < len) {
+ uint16_t word = !match_system(big_endian) ? swap_bytes(data[pos]) : data[pos];
+ if((word &0xF800) == 0xD800) {
+ if(pos + 1 >= len) { return false; }
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return false; }
+ uint16_t next_word = !match_system(big_endian) ? swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return false; }
+ pos += 2;
+ } else {
+ pos++;
+ }
+ }
+ return true;
+}
+
+template <endianness big_endian>
+inline simdutf_warn_unused result validate_with_errors(const char16_t *buf, size_t len) noexcept {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ while (pos < len) {
+ uint16_t word = !match_system(big_endian) ? swap_bytes(data[pos]) : data[pos];
+ if((word & 0xF800) == 0xD800) {
+ if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); }
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ uint16_t next_word = !match_system(big_endian) ? swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ pos += 2;
+ } else {
+ pos++;
+ }
+ }
+ return result(error_code::SUCCESS, pos);
+}
+
+template <endianness big_endian>
+inline size_t count_code_points(const char16_t* buf, size_t len) {
+ // We are not BOM aware.
+ const uint16_t * p = reinterpret_cast<const uint16_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i];
+ counter += ((word & 0xFC00) != 0xDC00);
+ }
+ return counter;
+}
+
+template <endianness big_endian>
+inline size_t utf8_length_from_utf16(const char16_t* buf, size_t len) {
+ // We are not BOM aware.
+ const uint16_t * p = reinterpret_cast<const uint16_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i];
+ counter++; // ASCII
+ counter += static_cast<size_t>(word > 0x7F); // non-ASCII is at least 2 bytes, surrogates are 2*2 == 4 bytes
+ counter += static_cast<size_t>((word > 0x7FF && word <= 0xD7FF) || (word >= 0xE000)); // three-byte
+ }
+ return counter;
+}
+
+template <endianness big_endian>
+inline size_t utf32_length_from_utf16(const char16_t* buf, size_t len) {
+ // We are not BOM aware.
+ const uint16_t * p = reinterpret_cast<const uint16_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ uint16_t word = !match_system(big_endian) ? swap_bytes(p[i]) : p[i];
+ counter += ((word & 0xFC00) != 0xDC00);
+ }
+ return counter;
+}
+
+
+inline size_t latin1_length_from_utf16(size_t len) {
+ return len;
+}
+
+simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* out) {
+ const uint16_t * input = reinterpret_cast<const uint16_t *>(in);
+ uint16_t * output = reinterpret_cast<uint16_t *>(out);
+ for (size_t i = 0; i < size; i++) {
+ *output++ = uint16_t(input[i] >> 8 | input[i] << 8);
+ }
+}
+
+
+template <endianness big_endian>
+simdutf_warn_unused inline size_t trim_partial_utf16(const char16_t* input, size_t length) {
+ if (length <= 1) {
+ return length;
+ }
+ uint16_t last_word = uint16_t(input[length-1]);
+ last_word = !match_system(big_endian) ? swap_bytes(last_word) : last_word;
+ length -= ((last_word & 0xFC00) == 0xD800);
+ return length;
+}
+
+} // utf16 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16.h */
+/* begin file src/scalar/utf32.h */
+#ifndef SIMDUTF_UTF32_H
+#define SIMDUTF_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32 {
+
+inline simdutf_warn_unused bool validate(const char32_t *buf, size_t len) noexcept {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ uint64_t pos = 0;
+ for(;pos < len; pos++) {
+ uint32_t word = data[pos];
+ if(word > 0x10FFFF || (word >= 0xD800 && word <= 0xDFFF)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+inline simdutf_warn_unused result validate_with_errors(const char32_t *buf, size_t len) noexcept {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ for(;pos < len; pos++) {
+ uint32_t word = data[pos];
+ if(word > 0x10FFFF) {
+ return result(error_code::TOO_LARGE, pos);
+ }
+ if(word >= 0xD800 && word <= 0xDFFF) {
+ return result(error_code::SURROGATE, pos);
+ }
+ }
+ return result(error_code::SUCCESS, pos);
+}
+
+inline size_t utf8_length_from_utf32(const char32_t* buf, size_t len) {
+ // We are not BOM aware.
+ const uint32_t * p = reinterpret_cast<const uint32_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ // credit: @ttsugriy for the vectorizable approach
+ counter++; // ASCII
+ counter += static_cast<size_t>(p[i] > 0x7F); // two-byte
+ counter += static_cast<size_t>(p[i] > 0x7FF); // three-byte
+ counter += static_cast<size_t>(p[i] > 0xFFFF); // four-bytes
+ }
+ return counter;
+}
+
+inline size_t utf16_length_from_utf32(const char32_t* buf, size_t len) {
+ // We are not BOM aware.
+ const uint32_t * p = reinterpret_cast<const uint32_t *>(buf);
+ size_t counter{0};
+ for(size_t i = 0; i < len; i++) {
+ counter++; // non-surrogate word
+ counter += static_cast<size_t>(p[i] > 0xFFFF); // surrogate pair
+ }
+ return counter;
+}
+
+inline size_t latin1_length_from_utf32(size_t len) {
+ // We are not BOM aware.
+ return len; // a utf32 codepoint will always represent 1 latin1 character
+}
+
+inline simdutf_warn_unused uint32_t swap_bytes(const uint32_t word) {
+ return ((word >> 24) & 0xff) | // move byte 3 to byte 0
+ ((word << 8) & 0xff0000) | // move byte 1 to byte 2
+ ((word >> 8) & 0xff00) | // move byte 2 to byte 1
+ ((word << 24) & 0xff000000); // byte 0 to byte 3
+}
+
+} // utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32.h */
+/* begin file src/scalar/base64.h */
+#ifndef SIMDUTF_BASE64_H
+#define SIMDUTF_BASE64_H
+
+#include <cstddef>
+#include <cstdint>
+#include <cstring>
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace base64 {
+
+// This function is not expected to be fast. Do not use in long loops.
+template <class char_type>
+bool is_ascii_white_space(char_type c) {
+ return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\f';
+}
+
+// Returns true upon success. The destination buffer must be large enough.
+// This functions assumes that the padding (=) has been removed.
+template <class char_type>
+result base64_tail_decode(char *dst, const char_type *src, size_t length, base64_options options) {
+ // This looks like 5 branches, but we expect the compiler to resolve this to a single branch:
+ const uint8_t *to_base64 = (options & base64_url) ? tables::base64::to_base64_url_value : tables::base64::to_base64_value;
+ const uint32_t *d0 = (options & base64_url) ? tables::base64::base64_url::d0 : tables::base64::base64_default::d0;
+ const uint32_t *d1 = (options & base64_url) ? tables::base64::base64_url::d1 : tables::base64::base64_default::d1;
+ const uint32_t *d2 = (options & base64_url) ? tables::base64::base64_url::d2 : tables::base64::base64_default::d2;
+ const uint32_t *d3 = (options & base64_url) ? tables::base64::base64_url::d3 : tables::base64::base64_default::d3;
+
+ const char_type *srcend = src + length;
+ const char_type *srcinit = src;
+ const char *dstinit = dst;
+
+ uint32_t x;
+ size_t idx;
+ uint8_t buffer[4];
+ while (true) {
+ while (src + 4 <= srcend &&
+ (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
+ d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
+ if(match_system(endianness::BIG)) {
+ x = scalar::utf32::swap_bytes(x);
+ }
+ std::memcpy(dst, &x, 3); // optimization opportunity: copy 4 bytes
+ dst += 3;
+ src += 4;
+ }
+ idx = 0;
+ // we need at least four characters.
+ while (idx < 4 && src < srcend) {
+ char_type c = *src;
+ uint8_t code = to_base64[uint8_t(c)];
+ buffer[idx] = uint8_t(code);
+ if (code <= 63) {
+ idx++;
+ } else if (code > 64) {
+ return {INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ } else {
+ // We have a space or a newline. We ignore it.
+ }
+ src++;
+ }
+ if (idx != 4) {
+ if (idx == 2) {
+ uint32_t triple =
+ (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 1);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ }
+ dst += 1;
+
+ } else if (idx == 3) {
+ uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
+ (uint32_t(buffer[1]) << 2 * 6) +
+ (uint32_t(buffer[2]) << 1 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 2);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 2);
+ }
+ dst += 2;
+ } else if (idx == 1) {
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ return {SUCCESS, size_t(dst - dstinit)};
+ }
+
+ uint32_t triple =
+ (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
+ (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 3);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 3);
+ }
+ dst += 3;
+ }
+}
+
+// like base64_tail_decode, but it will not write past the end of the ouput buffer.
+// outlen is modified to reflect the number of bytes written.
+// This functions assumes that the padding (=) has been removed.
+template <class char_type>
+result base64_tail_decode_safe(char *dst, size_t& outlen, const char_type *src, size_t length, base64_options options) {
+ // This looks like 5 branches, but we expect the compiler to resolve this to a single branch:
+ const uint8_t *to_base64 = (options & base64_url) ? tables::base64::to_base64_url_value : tables::base64::to_base64_value;
+ const uint32_t *d0 = (options & base64_url) ? tables::base64::base64_url::d0 : tables::base64::base64_default::d0;
+ const uint32_t *d1 = (options & base64_url) ? tables::base64::base64_url::d1 : tables::base64::base64_default::d1;
+ const uint32_t *d2 = (options & base64_url) ? tables::base64::base64_url::d2 : tables::base64::base64_default::d2;
+ const uint32_t *d3 = (options & base64_url) ? tables::base64::base64_url::d3 : tables::base64::base64_default::d3;
+
+ const char_type *srcend = src + length;
+ const char_type *srcinit = src;
+ const char *dstinit = dst;
+ const char *dstend = dst + outlen;
+
+ uint32_t x;
+ size_t idx;
+ uint8_t buffer[4];
+ while (true) {
+ while (src + 4 <= srcend &&
+ (x = d0[uint8_t(src[0])] | d1[uint8_t(src[1])] |
+ d2[uint8_t(src[2])] | d3[uint8_t(src[3])]) < 0x01FFFFFF) {
+ if(match_system(endianness::BIG)) {
+ x = scalar::utf32::swap_bytes(x);
+ }
+ if(dst + 3 > dstend) {
+ outlen = size_t(dst - dstinit);
+ return {OUTPUT_BUFFER_TOO_SMALL, size_t(src - srcinit)};
+ }
+ std::memcpy(dst, &x, 3); // optimization opportunity: copy 4 bytes
+ dst += 3;
+ src += 4;
+ }
+ idx = 0;
+ const char_type *srccur = src;
+
+ // we need at least four characters.
+ while (idx < 4 && src < srcend) {
+ char_type c = *src;
+ uint8_t code = to_base64[uint8_t(c)];
+ buffer[idx] = uint8_t(code);
+ if (code <= 63) {
+ idx++;
+ } else if (code > 64) {
+ outlen = size_t(dst - dstinit);
+ return {INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ } else {
+ // We have a space or a newline. We ignore it.
+ }
+ src++;
+ }
+ if (idx != 4) {
+ if (idx == 2) {
+ if(dst == dstend) {
+ outlen = size_t(dst - dstinit);
+ return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)};
+ }
+ uint32_t triple =
+ (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 1);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ }
+ dst += 1;
+
+ } else if (idx == 3) {
+ if(dst + 2 >= dstend) {
+ outlen = size_t(dst - dstinit);
+ return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)};
+ }
+ uint32_t triple = (uint32_t(buffer[0]) << 3 * 6) +
+ (uint32_t(buffer[1]) << 2 * 6) +
+ (uint32_t(buffer[2]) << 1 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 2);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 2);
+ }
+ dst += 2;
+ } else if (idx == 1) {
+ outlen = size_t(dst - dstinit);
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ outlen = size_t(dst - dstinit);
+ return {SUCCESS, size_t(dst - dstinit)};
+ }
+ if(dst + 3 >= dstend) {
+ outlen = size_t(dst - dstinit);
+ return {OUTPUT_BUFFER_TOO_SMALL, size_t(srccur - srcinit)};
+ }
+ uint32_t triple =
+ (uint32_t(buffer[0]) << 3 * 6) + (uint32_t(buffer[1]) << 2 * 6) +
+ (uint32_t(buffer[2]) << 1 * 6) + (uint32_t(buffer[3]) << 0 * 6);
+ if(match_system(endianness::BIG)) {
+ triple <<= 8;
+ std::memcpy(dst, &triple, 3);
+ } else {
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 3);
+ }
+ dst += 3;
+ }
+}
+
+// Returns the number of bytes written. The destination buffer must be large
+// enough. It will add padding (=) if needed.
+size_t tail_encode_base64(char *dst, const char *src, size_t srclen, base64_options options) {
+ // This looks like 3 branches, but we expect the compiler to resolve this to a single branch:
+ const char *e0 = (options & base64_url) ? tables::base64::base64_url::e0 : tables::base64::base64_default::e0;
+ const char *e1 = (options & base64_url) ? tables::base64::base64_url::e1 : tables::base64::base64_default::e1;
+ const char *e2 = (options & base64_url) ? tables::base64::base64_url::e2 : tables::base64::base64_default::e2;
+ char *out = dst;
+ size_t i = 0;
+ uint8_t t1, t2, t3;
+ for (; i + 2 < srclen; i += 3) {
+ t1 = uint8_t(src[i]);
+ t2 = uint8_t(src[i + 1]);
+ t3 = uint8_t(src[i + 2]);
+ *out++ = e0[t1];
+ *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
+ *out++ = e1[((t2 & 0x0F) << 2) | ((t3 >> 6) & 0x03)];
+ *out++ = e2[t3];
+ }
+ switch (srclen - i) {
+ case 0:
+ break;
+ case 1:
+ t1 = uint8_t(src[i]);
+ *out++ = e0[t1];
+ *out++ = e1[(t1 & 0x03) << 4];
+ if((options & base64_url) == 0) {
+ *out++ = '=';
+ *out++ = '=';
+ }
+ break;
+ default: /* case 2 */
+ t1 = uint8_t(src[i]);
+ t2 = uint8_t(src[i + 1]);
+ *out++ = e0[t1];
+ *out++ = e1[((t1 & 0x03) << 4) | ((t2 >> 4) & 0x0F)];
+ *out++ = e2[(t2 & 0x0F) << 2];
+ if((options & base64_url) == 0) {
+ *out++ = '=';
+ }
+ }
+ return (size_t)(out - dst);
+}
+
+template <class char_type>
+simdutf_warn_unused size_t maximal_binary_length_from_base64(const char_type * input, size_t length) noexcept {
+ // We follow https://infra.spec.whatwg.org/#forgiving-base64-decode
+ size_t padding = 0;
+ if(length > 0) {
+ if(input[length - 1] == '=') {
+ padding++;
+ if(length > 1 && input[length - 2] == '=') {
+ padding++;
+ }
+ }
+ }
+ size_t actual_length = length - padding;
+ if(actual_length % 4 <= 1) {
+ return actual_length / 4 * 3;
+ }
+ // if we have a valid input, then the remainder must be 2 or 3 adding one or two extra bytes.
+ return actual_length / 4 * 3 + (actual_length %4) - 1;
+}
+
+simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) noexcept {
+ if(options & base64_url) {
+ return length/3 * 4 + ((length % 3) ? (length % 3) + 1 : 0);
+ }
+ return (length + 2)/3 * 4; // We use padding to make the length a multiple of 4.
+}
+
+} // namespace base64
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/base64.h */
+
+namespace simdutf {
+bool implementation::supported_by_runtime_system() const {
+ uint32_t required_instruction_sets = this->required_instruction_sets();
+ uint32_t supported_instruction_sets = internal::detect_supported_architectures();
+ return ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets);
+}
+
+simdutf_warn_unused encoding_type implementation::autodetect_encoding(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ // UTF8 is common, it includes ASCII, and is commonly represented
+ // without a BOM, so if it fits, go with that. Note that it is still
+ // possible to get it wrong, we are only 'guessing'. If some has UTF-16
+ // data without a BOM, it could pass as UTF-8.
+ //
+ // An interesting twist might be to check for UTF-16 ASCII first (every
+ // other byte is zero).
+ if(validate_utf8(input, length)) { return encoding_type::UTF8; }
+ // The next most common encoding that might appear without BOM is probably
+ // UTF-16LE, so try that next.
+ if((length % 2) == 0) {
+ // important: we need to divide by two
+ if(validate_utf16le(reinterpret_cast<const char16_t*>(input), length/2)) { return encoding_type::UTF16_LE; }
+ }
+ if((length % 4) == 0) {
+ if(validate_utf32(reinterpret_cast<const char32_t*>(input), length/4)) { return encoding_type::UTF32_LE; }
+ }
+ return encoding_type::unspecified;
+}
+
+namespace internal {
+// When there is a single implementation, we should not pay a price
+ // for dispatching to the best implementation. We should just use the
+ // one we have. This is a compile-time check.
+ #define SIMDUTF_SINGLE_IMPLEMENTATION (SIMDUTF_IMPLEMENTATION_ICELAKE \
+ + SIMDUTF_IMPLEMENTATION_HASWELL + SIMDUTF_IMPLEMENTATION_WESTMERE \
+ + SIMDUTF_IMPLEMENTATION_ARM64 + SIMDUTF_IMPLEMENTATION_PPC64 \
+ + SIMDUTF_IMPLEMENTATION_FALLBACK == 1)
+
+// Static array of known implementations. We're hoping these get baked into the executable
+// without requiring a static initializer.
+
+
+#if SIMDUTF_IMPLEMENTATION_ICELAKE
+static const icelake::implementation* get_icelake_singleton() {
+ static const icelake::implementation icelake_singleton{};
+ return &icelake_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_HASWELL
+static const haswell::implementation* get_haswell_singleton() {
+ static const haswell::implementation haswell_singleton{};
+ return &haswell_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_WESTMERE
+static const westmere::implementation* get_westmere_singleton() {
+ static const westmere::implementation westmere_singleton{};
+ return &westmere_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_ARM64
+static const arm64::implementation* get_arm64_singleton() {
+ static const arm64::implementation arm64_singleton{};
+ return &arm64_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_PPC64
+static const ppc64::implementation* get_ppc64_singleton() {
+ static const ppc64::implementation ppc64_singleton{};
+ return &ppc64_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_RVV
+static const rvv::implementation* get_rvv_singleton() {
+ static const rvv::implementation rvv_singleton{};
+ return &rvv_singleton;
+}
+#endif
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+static const fallback::implementation* get_fallback_singleton() {
+ static const fallback::implementation fallback_singleton{};
+ return &fallback_singleton;
+}
+#endif
+
+#if SIMDUTF_SINGLE_IMPLEMENTATION
+static const implementation* get_single_implementation() {
+ return
+#if SIMDUTF_IMPLEMENTATION_ICELAKE
+ get_icelake_singleton();
+#endif
+#if SIMDUTF_IMPLEMENTATION_HASWELL
+ get_haswell_singleton();
+#endif
+#if SIMDUTF_IMPLEMENTATION_WESTMERE
+ get_westmere_singleton();
+#endif
+#if SIMDUTF_IMPLEMENTATION_ARM64
+ get_arm64_singleton();
+#endif
+#if SIMDUTF_IMPLEMENTATION_PPC64
+ get_ppc64_singleton();
+#endif
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+ get_fallback_singleton();
+#endif
+}
+#endif
+
+/**
+ * @private Detects best supported implementation on first use, and sets it
+ */
+class detect_best_supported_implementation_on_first_use final : public implementation {
+public:
+ std::string name() const noexcept final { return set_best()->name(); }
+ std::string description() const noexcept final { return set_best()->description(); }
+ uint32_t required_instruction_sets() const noexcept final { return set_best()->required_instruction_sets(); }
+
+ simdutf_warn_unused int detect_encodings(const char * input, size_t length) const noexcept override {
+ return set_best()->detect_encodings(input, length);
+ }
+
+ simdutf_warn_unused bool validate_utf8(const char * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf8(buf, len);
+ }
+
+ simdutf_warn_unused result validate_utf8_with_errors(const char * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf8_with_errors(buf, len);
+ }
+
+ simdutf_warn_unused bool validate_ascii(const char * buf, size_t len) const noexcept final override {
+ return set_best()->validate_ascii(buf, len);
+ }
+
+ simdutf_warn_unused result validate_ascii_with_errors(const char * buf, size_t len) const noexcept final override {
+ return set_best()->validate_ascii_with_errors(buf, len);
+ }
+
+ simdutf_warn_unused bool validate_utf16le(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf16le(buf, len);
+ }
+
+ simdutf_warn_unused bool validate_utf16be(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf16be(buf, len);
+ }
+
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf16le_with_errors(buf, len);
+ }
+
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf16be_with_errors(buf, len);
+ }
+
+ simdutf_warn_unused bool validate_utf32(const char32_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf32(buf, len);
+ }
+
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t * buf, size_t len) const noexcept final override {
+ return set_best()->validate_utf32_with_errors(buf, len);
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_latin1_to_utf8(buf, len,utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_latin1_to_utf16le(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_latin1_to_utf16be(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t * latin1_output) const noexcept final override {
+ return set_best()->convert_latin1_to_utf32(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf8_to_latin1(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf8_to_latin1_with_errors(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_valid_utf8_to_latin1(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf16le(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf16be(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf16le_with_errors(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf16be_with_errors(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_valid_utf8_to_utf16le(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_valid_utf8_to_utf16be(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf32(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf8_to_utf32_with_errors(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_valid_utf8_to_utf32(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_latin1(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_latin1(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_latin1_with_errors(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_latin1_with_errors(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_valid_utf16le_to_latin1(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_valid_utf16be_to_latin1(buf, len, latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_utf8_with_errors(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_utf8_with_errors(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_valid_utf16le_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_valid_utf16be_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf32_to_latin1(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf32_to_latin1_with_errors(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * buf, size_t len, char* latin1_output) const noexcept final override {
+ return set_best()->convert_utf32_to_latin1(buf, len,latin1_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_output) const noexcept final override {
+ return set_best()->convert_valid_utf32_to_utf8(buf, len, utf8_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf16le(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf16be(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf16le_with_errors(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_utf32_to_utf16be_with_errors(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_valid_utf32_to_utf16le(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_output) const noexcept final override {
+ return set_best()->convert_valid_utf32_to_utf16be(buf, len, utf16_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_utf32(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_utf32(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf16le_to_utf32_with_errors(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_utf16be_to_utf32_with_errors(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_valid_utf16le_to_utf32(buf, len, utf32_output);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_output) const noexcept final override {
+ return set_best()->convert_valid_utf16be_to_utf32(buf, len, utf32_output);
+ }
+
+ void change_endianness_utf16(const char16_t * buf, size_t len, char16_t * output) const noexcept final override {
+ set_best()->change_endianness_utf16(buf, len, output);
+ }
+
+ simdutf_warn_unused size_t count_utf16le(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->count_utf16le(buf, len);
+ }
+
+ simdutf_warn_unused size_t count_utf16be(const char16_t * buf, size_t len) const noexcept final override {
+ return set_best()->count_utf16be(buf, len);
+ }
+
+ simdutf_warn_unused size_t count_utf8(const char * buf, size_t len) const noexcept final override {
+ return set_best()->count_utf8(buf, len);
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char * buf, size_t len) const noexcept override {
+ return set_best()->latin1_length_from_utf8(buf, len);
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) const noexcept override {
+ return set_best()->latin1_length_from_utf16(len);
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) const noexcept override {
+ return set_best()->latin1_length_from_utf32(len);
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char * buf, size_t len) const noexcept override {
+ return set_best()->utf8_length_from_latin1(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * buf, size_t len) const noexcept override {
+ return set_best()->utf8_length_from_utf16le(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * buf, size_t len) const noexcept override {
+ return set_best()->utf8_length_from_utf16be(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t len) const noexcept override {
+ return set_best()->utf16_length_from_latin1(len);
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t len) const noexcept override {
+ return set_best()->utf32_length_from_latin1(len);
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * buf, size_t len) const noexcept override {
+ return set_best()->utf32_length_from_utf16le(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * buf, size_t len) const noexcept override {
+ return set_best()->utf32_length_from_utf16be(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char * buf, size_t len) const noexcept override {
+ return set_best()->utf16_length_from_utf8(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * buf, size_t len) const noexcept override {
+ return set_best()->utf8_length_from_utf32(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * buf, size_t len) const noexcept override {
+ return set_best()->utf16_length_from_utf32(buf, len);
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char * buf, size_t len) const noexcept override {
+ return set_best()->utf32_length_from_utf8(buf, len);
+ }
+
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept override {
+ return set_best()->maximal_binary_length_from_base64(input, length);
+ }
+
+ simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept override {
+ return set_best()->base64_to_binary(input, length, output, options);
+ }
+
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept override {
+ return set_best()->maximal_binary_length_from_base64(input, length);
+ }
+
+ simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept override {
+ return set_best()->base64_to_binary(input, length, output, options);
+ }
+
+ simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) const noexcept override {
+ return set_best()->base64_length_from_binary(length, options);
+ }
+
+ size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept override {
+ return set_best()->binary_to_base64(input, length, output, options);
+ }
+
+ simdutf_really_inline detect_best_supported_implementation_on_first_use() noexcept : implementation("best_supported_detector", "Detects the best supported implementation and sets it", 0) {}
+
+private:
+ const implementation *set_best() const noexcept;
+};
+
+static_assert(std::is_trivially_destructible<detect_best_supported_implementation_on_first_use>::value, "detect_best_supported_implementation_on_first_use should be trivially destructible");
+
+static const std::initializer_list<const implementation *>& get_available_implementation_pointers() {
+ static const std::initializer_list<const implementation *> available_implementation_pointers {
+#if SIMDUTF_IMPLEMENTATION_ICELAKE
+ get_icelake_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_HASWELL
+ get_haswell_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_WESTMERE
+ get_westmere_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_ARM64
+ get_arm64_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_PPC64
+ get_ppc64_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_RVV
+ get_rvv_singleton(),
+#endif
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+ get_fallback_singleton(),
+#endif
+ }; // available_implementation_pointers
+ return available_implementation_pointers;
+}
+
+// So we can return UNSUPPORTED_ARCHITECTURE from the parser when there is no support
+class unsupported_implementation final : public implementation {
+public:
+ simdutf_warn_unused int detect_encodings(const char *, size_t) const noexcept override {
+ return encoding_type::unspecified;
+ }
+
+ simdutf_warn_unused bool validate_utf8(const char *, size_t) const noexcept final override {
+ return false; // Just refuse to validate. Given that we have a fallback implementation
+ // it seems unlikely that unsupported_implementation will ever be used. If it is used,
+ // then it will flag all strings as invalid. The alternative is to return an error_code
+ // from which the user has to figure out whether the string is valid UTF-8... which seems
+ // like a lot of work just to handle the very unlikely case that we have an unsupported
+ // implementation. And, when it does happen (that we have an unsupported implementation),
+ // what are the chances that the programmer has a fallback? Given that *we* provide the
+ // fallback, it implies that the programmer would need a fallback for our fallback.
+ }
+
+ simdutf_warn_unused result validate_utf8_with_errors(const char *, size_t) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused bool validate_ascii(const char *, size_t) const noexcept final override {
+ return false;
+ }
+
+ simdutf_warn_unused result validate_ascii_with_errors(const char *, size_t) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused bool validate_utf16le(const char16_t*, size_t) const noexcept final override {
+ return false;
+ }
+
+ simdutf_warn_unused bool validate_utf16be(const char16_t*, size_t) const noexcept final override {
+ return false;
+ }
+
+ simdutf_warn_unused result validate_utf16le_with_errors(const char16_t*, size_t) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result validate_utf16be_with_errors(const char16_t*, size_t) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused bool validate_utf32(const char32_t*, size_t) const noexcept final override {
+ return false;
+ }
+
+ simdutf_warn_unused result validate_utf32_with_errors(const char32_t*, size_t) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf16le(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf16be(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char*, size_t, char16_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char*, size_t, char16_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf8_to_utf32(const char*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char*, size_t, char32_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t *, size_t, char* ) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t *, size_t, char* ) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t *, size_t, char* ) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t*, size_t, char*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t*, size_t, char*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t*, size_t, char16_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t*, size_t, char32_t*) const noexcept final override {
+ return 0;
+ }
+
+ void change_endianness_utf16(const char16_t *, size_t, char16_t *) const noexcept final override {
+
+ }
+
+ simdutf_warn_unused size_t count_utf16le(const char16_t *, size_t) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t count_utf16be(const char16_t *, size_t) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t count_utf8(const char *, size_t) const noexcept final override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf8(const char *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf16(size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t latin1_length_from_utf32(size_t) const noexcept override {
+ return 0;
+ }
+ simdutf_warn_unused size_t utf8_length_from_latin1(const char *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_latin1(size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf16_length_from_utf8(const char *, size_t) const noexcept override {
+ return 0;
+ }
+ simdutf_warn_unused size_t utf16_length_from_latin1(size_t) const noexcept override {
+ return 0;
+ }
+ simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t utf32_length_from_utf8(const char *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused result base64_to_binary(const char *, size_t, char*, base64_options) const noexcept override {
+ return result(error_code::OTHER, 0);
+ }
+
+ simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t *, size_t) const noexcept override {
+ return 0;
+ }
+
+ simdutf_warn_unused result base64_to_binary(const char16_t *, size_t, char*, base64_options) const noexcept override {
+ return result(error_code::OTHER, 0);
+ }
+
+
+ simdutf_warn_unused size_t base64_length_from_binary(size_t, base64_options) const noexcept override {
+ return 0;
+ }
+
+ size_t binary_to_base64(const char *, size_t, char*, base64_options) const noexcept override {
+ return 0;
+ }
+
+ unsupported_implementation() : implementation("unsupported", "Unsupported CPU (no detected SIMD instructions)", 0) {}
+};
+
+const unsupported_implementation* get_unsupported_singleton() {
+ static const unsupported_implementation unsupported_singleton{};
+ return &unsupported_singleton;
+}
+static_assert(std::is_trivially_destructible<unsupported_implementation>::value, "unsupported_singleton should be trivially destructible");
+
+size_t available_implementation_list::size() const noexcept {
+ return internal::get_available_implementation_pointers().size();
+}
+const implementation * const *available_implementation_list::begin() const noexcept {
+ return internal::get_available_implementation_pointers().begin();
+}
+const implementation * const *available_implementation_list::end() const noexcept {
+ return internal::get_available_implementation_pointers().end();
+}
+const implementation *available_implementation_list::detect_best_supported() const noexcept {
+ // They are prelisted in priority order, so we just go down the list
+ uint32_t supported_instruction_sets = internal::detect_supported_architectures();
+ for (const implementation *impl : internal::get_available_implementation_pointers()) {
+ uint32_t required_instruction_sets = impl->required_instruction_sets();
+ if ((supported_instruction_sets & required_instruction_sets) == required_instruction_sets) { return impl; }
+ }
+ return get_unsupported_singleton(); // this should never happen?
+}
+
+const implementation *detect_best_supported_implementation_on_first_use::set_best() const noexcept {
+ SIMDUTF_PUSH_DISABLE_WARNINGS
+ SIMDUTF_DISABLE_DEPRECATED_WARNING // Disable CRT_SECURE warning on MSVC: manually verified this is safe
+ char *force_implementation_name = getenv("SIMDUTF_FORCE_IMPLEMENTATION");
+ SIMDUTF_POP_DISABLE_WARNINGS
+
+ if (force_implementation_name) {
+ auto force_implementation = get_available_implementations()[force_implementation_name];
+ if (force_implementation) {
+ return get_active_implementation() = force_implementation;
+ } else {
+ // Note: abort() and stderr usage within the library is forbidden.
+ return get_active_implementation() = get_unsupported_singleton();
+ }
+ }
+ return get_active_implementation() = get_available_implementations().detect_best_supported();
+}
+
+} // namespace internal
+
+
+
+/**
+ * The list of available implementations compiled into simdutf.
+ */
+SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations() {
+ static const internal::available_implementation_list available_implementations{};
+ return available_implementations;
+}
+
+/**
+ * The active implementation.
+ */
+SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation() {
+#if SIMDUTF_SINGLE_IMPLEMENTATION
+ // skip runtime detection
+ static internal::atomic_ptr<const implementation> active_implementation{internal::get_single_implementation()};
+ return active_implementation;
+#else
+ static const internal::detect_best_supported_implementation_on_first_use detect_best_supported_implementation_on_first_use_singleton;
+ static internal::atomic_ptr<const implementation> active_implementation{&detect_best_supported_implementation_on_first_use_singleton};
+ return active_implementation;
+#endif
+}
+
+
+#if SIMDUTF_SINGLE_IMPLEMENTATION
+const implementation * get_default_implementation() {
+ return internal::get_single_implementation();
+}
+#else
+internal::atomic_ptr<const implementation>& get_default_implementation() {
+ return get_active_implementation();
+}
+#endif
+#define SIMDUTF_GET_CURRENT_IMPLEMENTION
+
+simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf8(buf, len);
+}
+simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf8_with_errors(buf, len);
+}
+simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept {
+ return get_default_implementation()->validate_ascii(buf, len);
+}
+simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) noexcept {
+ return get_default_implementation()->validate_ascii_with_errors(buf, len);
+}
+simdutf_warn_unused size_t convert_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf8_to_utf16be(input, length, utf16_output);
+ #else
+ return convert_utf8_to_utf16le(input, length, utf16_output);
+ #endif
+}
+simdutf_warn_unused size_t convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) noexcept {
+ return get_default_implementation()->convert_latin1_to_utf8(buf, len,utf8_output);
+}
+simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * buf, size_t len, char16_t* utf16_output) noexcept {
+ return get_default_implementation()->convert_latin1_to_utf16le(buf, len, utf16_output);
+}
+simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * buf, size_t len, char16_t* utf16_output) noexcept{
+ return get_default_implementation()->convert_latin1_to_utf16be(buf, len, utf16_output);
+}
+simdutf_warn_unused size_t convert_latin1_to_utf32(const char * buf, size_t len, char32_t * latin1_output) noexcept {
+ return get_default_implementation()->convert_latin1_to_utf32(buf, len,latin1_output);
+}
+simdutf_warn_unused size_t convert_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_latin1(buf, len,latin1_output);
+}
+simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_latin1_with_errors(buf, len, latin1_output);
+}
+simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * buf, size_t len, char* latin1_output) noexcept {
+ return get_default_implementation()->convert_valid_utf8_to_latin1(buf, len,latin1_output);
+}
+simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf16le(input, length, utf16_output);
+}
+simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf16be(input, length, utf16_output);
+}
+simdutf_warn_unused result convert_utf8_to_utf16_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf8_to_utf16be_with_errors(input, length, utf16_output);
+ #else
+ return convert_utf8_to_utf16le_with_errors(input, length, utf16_output);
+ #endif
+}
+simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf16le_with_errors(input, length, utf16_output);
+}
+simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf16be_with_errors(input, length, utf16_output);
+}
+simdutf_warn_unused size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf32(input, length, utf32_output);
+}
+simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) noexcept {
+ return get_default_implementation()->convert_utf8_to_utf32_with_errors(input, length, utf32_output);
+}
+simdutf_warn_unused bool validate_utf16(const char16_t * buf, size_t len) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return validate_utf16be(buf, len);
+ #else
+ return validate_utf16le(buf, len);
+ #endif
+}
+simdutf_warn_unused bool validate_utf16le(const char16_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf16le(buf, len);
+}
+simdutf_warn_unused bool validate_utf16be(const char16_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf16be(buf, len);
+}
+simdutf_warn_unused result validate_utf16_with_errors(const char16_t * buf, size_t len) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return validate_utf16be_with_errors(buf, len);
+ #else
+ return validate_utf16le_with_errors(buf, len);
+ #endif
+}
+simdutf_warn_unused result validate_utf16le_with_errors(const char16_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf16le_with_errors(buf, len);
+}
+simdutf_warn_unused result validate_utf16be_with_errors(const char16_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf16be_with_errors(buf, len);
+}
+simdutf_warn_unused bool validate_utf32(const char32_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf32(buf, len);
+}
+simdutf_warn_unused result validate_utf32_with_errors(const char32_t * buf, size_t len) noexcept {
+ return get_default_implementation()->validate_utf32_with_errors(buf, len);
+}
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_valid_utf8_to_utf16be(input, length, utf16_buffer);
+ #else
+ return convert_valid_utf8_to_utf16le(input, length, utf16_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf8_to_utf16le(input, length, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf8_to_utf16be(input, length, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf8_to_utf32(input, length, utf32_buffer);
+}
+simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_utf8(buf, len, utf8_buffer);
+ #else
+ return convert_utf16le_to_utf8(buf, len, utf8_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_utf16_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_latin1(buf, len, latin1_buffer);
+ #else
+ return convert_utf16le_to_latin1(buf, len, latin1_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_latin1_to_utf16(const char * buf, size_t len, char16_t* utf16_output) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_latin1_to_utf16be(buf, len, utf16_output);
+ #else
+ return convert_latin1_to_utf16le(buf, len, utf16_output);
+ #endif
+}
+simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_latin1(buf, len, latin1_buffer);
+}
+simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_latin1(buf, len, latin1_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16be_to_latin1(buf, len, latin1_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16le_to_latin1(buf, len, latin1_buffer);
+}
+simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_latin1_with_errors(buf, len, latin1_buffer);
+}
+simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_latin1_with_errors(buf, len, latin1_buffer);
+}
+simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused result convert_utf16_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_utf8_with_errors(buf, len, utf8_buffer);
+ #else
+ return convert_utf16le_to_utf8_with_errors(buf, len, utf8_buffer);
+ #endif
+}
+simdutf_warn_unused result convert_utf16_to_latin1_with_errors(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_latin1_with_errors(buf, len, latin1_buffer);
+ #else
+ return convert_utf16le_to_latin1_with_errors(buf, len, latin1_buffer);
+ #endif
+}
+simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_utf8_with_errors(buf, len, utf8_buffer);
+}
+simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_utf8_with_errors(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_valid_utf16be_to_utf8(buf, len, utf8_buffer);
+ #else
+ return convert_valid_utf16le_to_utf8(buf, len, utf8_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_valid_utf16_to_latin1(const char16_t * buf, size_t len, char* latin1_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_valid_utf16be_to_latin1(buf, len, latin1_buffer);
+ #else
+ return convert_valid_utf16le_to_latin1(buf, len, latin1_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16le_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16be_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf8_with_errors(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * buf, size_t len, char* utf8_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf32_to_utf8(buf, len, utf8_buffer);
+}
+simdutf_warn_unused size_t convert_utf32_to_utf16(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf32_to_utf16be(buf, len, utf16_buffer);
+ #else
+ return convert_utf32_to_utf16le(buf, len, utf16_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_output) noexcept {
+ return get_default_implementation()->convert_utf32_to_latin1(input, length, latin1_output);
+}
+simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf16le(buf, len, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf16be(buf, len, utf16_buffer);
+}
+simdutf_warn_unused result convert_utf32_to_utf16_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf32_to_utf16be_with_errors(buf, len, utf16_buffer);
+ #else
+ return convert_utf32_to_utf16le_with_errors(buf, len, utf16_buffer);
+ #endif
+}
+simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf16le_with_errors(buf, len, utf16_buffer);
+}
+simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_utf32_to_utf16be_with_errors(buf, len, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_valid_utf32_to_utf16be(buf, len, utf16_buffer);
+ #else
+ return convert_valid_utf32_to_utf16le(buf, len, utf16_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf32_to_utf16le(buf, len, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * buf, size_t len, char16_t* utf16_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf32_to_utf16be(buf, len, utf16_buffer);
+}
+simdutf_warn_unused size_t convert_utf16_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_utf32(buf, len, utf32_buffer);
+ #else
+ return convert_utf16le_to_utf32(buf, len, utf32_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_utf32(buf, len, utf32_buffer);
+}
+simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_utf32(buf, len, utf32_buffer);
+}
+simdutf_warn_unused result convert_utf16_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_utf16be_to_utf32_with_errors(buf, len, utf32_buffer);
+ #else
+ return convert_utf16le_to_utf32_with_errors(buf, len, utf32_buffer);
+ #endif
+}
+simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_utf16le_to_utf32_with_errors(buf, len, utf32_buffer);
+}
+simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_utf16be_to_utf32_with_errors(buf, len, utf32_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return convert_valid_utf16be_to_utf32(buf, len, utf32_buffer);
+ #else
+ return convert_valid_utf16le_to_utf32(buf, len, utf32_buffer);
+ #endif
+}
+simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16le_to_utf32(buf, len, utf32_buffer);
+}
+simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * buf, size_t len, char32_t* utf32_buffer) noexcept {
+ return get_default_implementation()->convert_valid_utf16be_to_utf32(buf, len, utf32_buffer);
+}
+void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) noexcept {
+ get_default_implementation()->change_endianness_utf16(input, length, output);
+}
+simdutf_warn_unused size_t count_utf16(const char16_t * input, size_t length) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return count_utf16be(input, length);
+ #else
+ return count_utf16le(input, length);
+ #endif
+}
+simdutf_warn_unused size_t count_utf16le(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->count_utf16le(input, length);
+}
+simdutf_warn_unused size_t count_utf16be(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->count_utf16be(input, length);
+}
+simdutf_warn_unused size_t count_utf8(const char * input, size_t length) noexcept {
+ return get_default_implementation()->count_utf8(input, length);
+}
+simdutf_warn_unused size_t latin1_length_from_utf8(const char * buf, size_t len) noexcept {
+ return get_default_implementation()->latin1_length_from_utf8(buf, len);
+}
+simdutf_warn_unused size_t latin1_length_from_utf16(size_t len) noexcept {
+ return get_default_implementation()->latin1_length_from_utf16(len);
+}
+simdutf_warn_unused size_t latin1_length_from_utf32(size_t len) noexcept {
+ return get_default_implementation()->latin1_length_from_utf32(len);
+}
+simdutf_warn_unused size_t utf8_length_from_latin1(const char * buf, size_t len) noexcept {
+ return get_default_implementation()->utf8_length_from_latin1(buf, len);
+}
+simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t * input, size_t length) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return utf8_length_from_utf16be(input, length);
+ #else
+ return utf8_length_from_utf16le(input, length);
+ #endif
+}
+simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf8_length_from_utf16le(input, length);
+}
+simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf8_length_from_utf16be(input, length);
+}
+simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t * input, size_t length) noexcept {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return utf32_length_from_utf16be(input, length);
+ #else
+ return utf32_length_from_utf16le(input, length);
+ #endif
+}
+simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf32_length_from_utf16le(input, length);
+}
+simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf32_length_from_utf16be(input, length);
+}
+simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) noexcept {
+ return get_default_implementation()->utf16_length_from_utf8(input, length);
+}
+simdutf_warn_unused size_t utf16_length_from_latin1(size_t length) noexcept {
+ return get_default_implementation()->utf16_length_from_latin1(length);
+}
+simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf8_length_from_utf32(input, length);
+}
+simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) noexcept {
+ return get_default_implementation()->utf16_length_from_utf32(input, length);
+}
+simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) noexcept {
+ return get_default_implementation()->utf32_length_from_utf8(input, length);
+}
+
+simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) noexcept {
+ return get_default_implementation()->maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options) noexcept {
+ return get_default_implementation()->base64_to_binary(input, length, output, options);
+}
+
+simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) noexcept {
+ return get_default_implementation()->maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) noexcept {
+ return get_default_implementation()->base64_to_binary(input, length, output, options);
+}
+
+template <typename chartype>
+simdutf_warn_unused result base64_to_binary_safe_impl(const chartype * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept {
+ static_assert(std::is_same<chartype, char>::value || std::is_same<chartype, char16_t>::value, "Only char and char16_t are supported.");
+ // The implementation could be nicer, but we expect that most times, the user
+ // will provide us with a buffer that is large enough.
+ size_t max_length = maximal_binary_length_from_base64(input, length);
+ if(outlen >= max_length) {
+ // fast path
+ result r = base64_to_binary(input, length, output, options);
+ if(r.error != error_code::INVALID_BASE64_CHARACTER) { outlen = r.count; r.count = length; }
+ return r;
+ }
+ // The output buffer is maybe too small. We will decode a truncated version of the input.
+ size_t outlen3 = outlen / 3 * 3; // round down to multiple of 3
+ size_t safe_input = base64_length_from_binary(outlen3, options);
+ result r = base64_to_binary(input, safe_input, output, options);
+ if(r.error == error_code::INVALID_BASE64_CHARACTER) { return r; }
+ size_t offset = (r.error == error_code::BASE64_INPUT_REMAINDER) ? 1 :
+ ((r.count % 3) == 0 ? 0 : (r.count % 3) + 1);
+ size_t output_index = r.count - (r.count % 3);
+ size_t input_index = safe_input;
+ // offset is a value that is no larger than 3. We backtrack
+ // by up to offset characters + an undetermined number of
+ // white space characters. It is expected that the next loop
+ // runs at most 3 times + the number of white space characters
+ // in between them, so we are not worried about performance.
+ while(offset > 0 && input_index > 0) {
+ chartype c = input[--input_index];
+ if(scalar::base64::is_ascii_white_space(c)){
+ // skipping
+ } else {
+ offset--;
+ }
+ }
+ size_t remaining_out = outlen - output_index;
+ const chartype * tail_input = input + input_index;
+ size_t tail_length = length - input_index;
+ while(tail_length > 0 && scalar::base64::is_ascii_white_space(tail_input[tail_length - 1])) {
+ tail_length--;
+ }
+ size_t padding_characts = 0;
+ if(tail_length > 0 && tail_input[tail_length - 1] == '=') {
+ tail_length--;
+ padding_characts++;
+ while(tail_length > 0 && scalar::base64::is_ascii_white_space(tail_input[tail_length - 1])) {
+ tail_length--;
+ }
+ if(tail_length > 0 && tail_input[tail_length - 1] == '=') {
+ tail_length--;
+ padding_characts++;
+ }
+ }
+ r = scalar::base64::base64_tail_decode_safe(output + output_index, remaining_out, tail_input, tail_length, options);
+ outlen = output_index + remaining_out;
+ if(r.error == error_code::SUCCESS && padding_characts > 0) {
+ // additional checks
+ if((outlen % 3 == 0) || ((outlen % 3) + 1 + padding_characts != 4)) {
+ r.error = error_code::INVALID_BASE64_CHARACTER;
+ }
+ }
+ r.count += input_index;
+ return r;
+}
+
+
+simdutf_warn_unused result base64_to_binary_safe(const char * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept {
+ return base64_to_binary_safe_impl<char>(input, length, output, outlen, options);
+}
+simdutf_warn_unused result base64_to_binary_safe(const char16_t * input, size_t length, char* output, size_t& outlen, base64_options options) noexcept {
+ return base64_to_binary_safe_impl<char16_t>(input, length, output, outlen, options);
+}
+
+simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options) noexcept {
+ return get_default_implementation()->base64_length_from_binary(length, options);
+}
+
+size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options) noexcept {
+ return get_default_implementation()->binary_to_base64(input, length, output, options);
+}
+
+simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const char * buf, size_t length) noexcept {
+ return get_default_implementation()->autodetect_encoding(buf, length);
+}
+simdutf_warn_unused int detect_encodings(const char * buf, size_t length) noexcept {
+ return get_default_implementation()->detect_encodings(buf, length);
+}
+const implementation * builtin_implementation() {
+ static const implementation * builtin_impl = get_available_implementations()[SIMDUTF_STRINGIFY(SIMDUTF_BUILTIN_IMPLEMENTATION)];
+ return builtin_impl;
+}
+
+simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length) {
+ return scalar::utf8::trim_partial_utf8(input, length);
+}
+
+simdutf_warn_unused size_t trim_partial_utf16be(const char16_t* input, size_t length) {
+ return scalar::utf16::trim_partial_utf16<BIG>(input, length);
+}
+
+simdutf_warn_unused size_t trim_partial_utf16le(const char16_t* input, size_t length) {
+ return scalar::utf16::trim_partial_utf16<LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t trim_partial_utf16(const char16_t* input, size_t length) {
+ #if SIMDUTF_IS_BIG_ENDIAN
+ return trim_partial_utf16be(input, length);
+ #else
+ return trim_partial_utf16le(input, length);
+ #endif
+}
+
+} // namespace simdutf
+
+/* end file src/implementation.cpp */
+/* begin file src/encoding_types.cpp */
+
+namespace simdutf {
+bool match_system(endianness e) {
+#if SIMDUTF_IS_BIG_ENDIAN
+ return e == endianness::BIG;
+#else
+ return e == endianness::LITTLE;
+#endif
+}
+
+std::string to_string(encoding_type bom) {
+ switch (bom) {
+ case UTF16_LE: return "UTF16 little-endian";
+ case UTF16_BE: return "UTF16 big-endian";
+ case UTF32_LE: return "UTF32 little-endian";
+ case UTF32_BE: return "UTF32 big-endian";
+ case UTF8: return "UTF8";
+ case unspecified: return "unknown";
+ default: return "error";
+ }
+}
+
+namespace BOM {
+// Note that BOM for UTF8 is discouraged.
+encoding_type check_bom(const uint8_t* byte, size_t length) {
+ if (length >= 2 && byte[0] == 0xff and byte[1] == 0xfe) {
+ if (length >= 4 && byte[2] == 0x00 and byte[3] == 0x0) {
+ return encoding_type::UTF32_LE;
+ } else {
+ return encoding_type::UTF16_LE;
+ }
+ } else if (length >= 2 && byte[0] == 0xfe and byte[1] == 0xff) {
+ return encoding_type::UTF16_BE;
+ } else if (length >= 4 && byte[0] == 0x00 and byte[1] == 0x00 and byte[2] == 0xfe and byte[3] == 0xff) {
+ return encoding_type::UTF32_BE;
+ } else if (length >= 4 && byte[0] == 0xef and byte[1] == 0xbb and byte[2] == 0xbf) {
+ return encoding_type::UTF8;
+ }
+ return encoding_type::unspecified;
+ }
+
+encoding_type check_bom(const char* byte, size_t length) {
+ return check_bom(reinterpret_cast<const uint8_t*>(byte), length);
+ }
+
+ size_t bom_byte_size(encoding_type bom) {
+ switch (bom) {
+ case UTF16_LE: return 2;
+ case UTF16_BE: return 2;
+ case UTF32_LE: return 4;
+ case UTF32_BE: return 4;
+ case UTF8: return 3;
+ case unspecified: return 0;
+ default: return 0;
+ }
+}
+
+}
+}
+/* end file src/encoding_types.cpp */
+/* begin file src/error.cpp */
+namespace simdutf {
+// deliberately empty
+}
+/* end file src/error.cpp */
+// The large tables should be included once and they
+// should not depend on a kernel.
+/* begin file src/tables/utf8_to_utf16_tables.h */
+#ifndef SIMDUTF_UTF8_TO_UTF16_TABLES_H
+#define SIMDUTF_UTF8_TO_UTF16_TABLES_H
+#include <cstdint>
+
+namespace simdutf {
+namespace {
+namespace tables {
+namespace utf8_to_utf16 {
+/**
+ * utf8bigindex uses about 8 kB
+ * shufutf8 uses about 3344 B
+ *
+ * So we use a bit over 11 kB. It would be
+ * easy to save about 4 kB by only
+ * storing the index in utf8bigindex, and
+ * deriving the consumed bytes otherwise.
+ * However, this may come at a significant (10% to 20%)
+ * performance penalty.
+ */
+
+const uint8_t shufutf8[209][16] =
+{ {0, 255, 1, 255, 2, 255, 3, 255, 4, 255, 5, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 3, 255, 4, 255, 6, 5, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 3, 255, 5, 4, 6, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 3, 255, 5, 4, 7, 6, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 4, 3, 5, 255, 6, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 4, 3, 5, 255, 7, 6, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 4, 3, 6, 5, 7, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 2, 255, 4, 3, 6, 5, 8, 7, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0},
+ {0, 255, 1, 255, 3, 2, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 3, 255, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0},
+ {0, 255, 2, 1, 4, 3, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 4, 255, 5, 255, 6, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 4, 255, 5, 255, 7, 6, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 4, 255, 6, 5, 7, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 4, 255, 6, 5, 8, 7, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 5, 4, 6, 255, 7, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 5, 4, 6, 255, 8, 7, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 5, 4, 7, 6, 8, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 3, 255, 5, 4, 7, 6, 9, 8, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0},
+ {1, 0, 2, 255, 4, 3, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 5, 255, 6, 255, 7, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 5, 255, 6, 255, 8, 7, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 5, 255, 7, 6, 8, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 5, 255, 7, 6, 9, 8, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 6, 5, 7, 255, 8, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 6, 5, 7, 255, 9, 8, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 6, 5, 8, 7, 9, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 4, 255, 6, 5, 8, 7, 10, 9, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 6, 255, 7, 255, 8, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 6, 255, 7, 255, 9, 8, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 6, 255, 8, 7, 9, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 6, 255, 8, 7, 10, 9, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 7, 6, 8, 255, 9, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 7, 6, 8, 255, 10, 9, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 10, 255, 0, 0, 0, 0},
+ {1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 0, 0, 0, 0},
+ {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 4, 255, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 5, 4, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 6, 5, 4, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 5, 255, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 6, 5, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 7, 6, 5, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 6, 255, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 7, 6, 255, 255},
+ {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 8, 7, 6, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 5, 255, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 6, 5, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 7, 6, 5, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 6, 255, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 7, 6, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 8, 7, 6, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 7, 255, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 8, 7, 255, 255},
+ {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 9, 8, 7, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 4, 255, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 5, 4, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 6, 5, 4, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 5, 255, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 6, 5, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 7, 6, 5, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 6, 255, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 7, 6, 255, 255},
+ {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 8, 7, 6, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 5, 255, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 6, 5, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 7, 6, 5, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 6, 255, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 7, 6, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 8, 7, 6, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 7, 255, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 8, 7, 255, 255},
+ {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 9, 8, 7, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 6, 255, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 7, 6, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 8, 7, 6, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 7, 255, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 8, 7, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 9, 8, 7, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 8, 255, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 9, 8, 255, 255},
+ {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 10, 9, 8, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 5, 255, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 6, 5, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 7, 6, 5, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 6, 255, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 7, 6, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 8, 7, 6, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 7, 255, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 8, 7, 255, 255},
+ {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 9, 8, 7, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 6, 255, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 7, 6, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 8, 7, 6, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 7, 255, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 8, 7, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 9, 8, 7, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 8, 255, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 9, 8, 255, 255},
+ {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 10, 9, 8, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 7, 255, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 8, 7, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 9, 8, 7, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 8, 255, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 9, 8, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 10, 9, 8, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 9, 255, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 10, 9, 255, 255},
+ {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 11, 10, 9, 255},
+ {0, 255, 255, 255, 1, 255, 255, 255, 2, 255, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 1, 255, 255, 255, 3, 2, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 1, 255, 255, 255, 4, 3, 2, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 1, 255, 255, 255, 5, 4, 3, 2, 0, 0, 0, 0},
+ {0, 255, 255, 255, 2, 1, 255, 255, 3, 255, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 2, 1, 255, 255, 4, 3, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 2, 1, 255, 255, 5, 4, 3, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 2, 1, 255, 255, 6, 5, 4, 3, 0, 0, 0, 0},
+ {0, 255, 255, 255, 3, 2, 1, 255, 4, 255, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 3, 2, 1, 255, 5, 4, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 3, 2, 1, 255, 6, 5, 4, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 3, 2, 1, 255, 7, 6, 5, 4, 0, 0, 0, 0},
+ {0, 255, 255, 255, 4, 3, 2, 1, 5, 255, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 4, 3, 2, 1, 6, 5, 255, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 4, 3, 2, 1, 7, 6, 5, 255, 0, 0, 0, 0},
+ {0, 255, 255, 255, 4, 3, 2, 1, 8, 7, 6, 5, 0, 0, 0, 0},
+ {1, 0, 255, 255, 2, 255, 255, 255, 3, 255, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 2, 255, 255, 255, 4, 3, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 2, 255, 255, 255, 5, 4, 3, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 2, 255, 255, 255, 6, 5, 4, 3, 0, 0, 0, 0},
+ {1, 0, 255, 255, 3, 2, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 3, 2, 255, 255, 5, 4, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 3, 2, 255, 255, 6, 5, 4, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 3, 2, 255, 255, 7, 6, 5, 4, 0, 0, 0, 0},
+ {1, 0, 255, 255, 4, 3, 2, 255, 5, 255, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 4, 3, 2, 255, 6, 5, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 4, 3, 2, 255, 7, 6, 5, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 4, 3, 2, 255, 8, 7, 6, 5, 0, 0, 0, 0},
+ {1, 0, 255, 255, 5, 4, 3, 2, 6, 255, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 5, 4, 3, 2, 7, 6, 255, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 5, 4, 3, 2, 8, 7, 6, 255, 0, 0, 0, 0},
+ {1, 0, 255, 255, 5, 4, 3, 2, 9, 8, 7, 6, 0, 0, 0, 0},
+ {2, 1, 0, 255, 3, 255, 255, 255, 4, 255, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 3, 255, 255, 255, 5, 4, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 3, 255, 255, 255, 6, 5, 4, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 3, 255, 255, 255, 7, 6, 5, 4, 0, 0, 0, 0},
+ {2, 1, 0, 255, 4, 3, 255, 255, 5, 255, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 4, 3, 255, 255, 6, 5, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 4, 3, 255, 255, 7, 6, 5, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 4, 3, 255, 255, 8, 7, 6, 5, 0, 0, 0, 0},
+ {2, 1, 0, 255, 5, 4, 3, 255, 6, 255, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 5, 4, 3, 255, 7, 6, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 5, 4, 3, 255, 8, 7, 6, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 5, 4, 3, 255, 9, 8, 7, 6, 0, 0, 0, 0},
+ {2, 1, 0, 255, 6, 5, 4, 3, 7, 255, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 6, 5, 4, 3, 8, 7, 255, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 6, 5, 4, 3, 9, 8, 7, 255, 0, 0, 0, 0},
+ {2, 1, 0, 255, 6, 5, 4, 3, 10, 9, 8, 7, 0, 0, 0, 0},
+ {3, 2, 1, 0, 4, 255, 255, 255, 5, 255, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 4, 255, 255, 255, 6, 5, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 4, 255, 255, 255, 7, 6, 5, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 4, 255, 255, 255, 8, 7, 6, 5, 0, 0, 0, 0},
+ {3, 2, 1, 0, 5, 4, 255, 255, 6, 255, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 5, 4, 255, 255, 7, 6, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 5, 4, 255, 255, 8, 7, 6, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 5, 4, 255, 255, 9, 8, 7, 6, 0, 0, 0, 0},
+ {3, 2, 1, 0, 6, 5, 4, 255, 7, 255, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 6, 5, 4, 255, 8, 7, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 6, 5, 4, 255, 9, 8, 7, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 6, 5, 4, 255, 10, 9, 8, 7, 0, 0, 0, 0},
+ {3, 2, 1, 0, 7, 6, 5, 4, 8, 255, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 7, 6, 5, 4, 9, 8, 255, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 7, 6, 5, 4, 10, 9, 8, 255, 0, 0, 0, 0},
+ {3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 0, 0, 0, 0}};
+/* number of two bytes : 64 */
+/* number of two + three bytes : 145 */
+/* number of two + three + four bytes : 209 */
+const uint8_t utf8bigindex[4096][2] =
+{ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {148, 6},
+ {209, 12},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {209, 12},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {209, 12},
+ {155, 7},
+ {167, 7},
+ {69, 7},
+ {179, 7},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {170, 7},
+ {71, 7},
+ {182, 7},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {185, 7},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {171, 8},
+ {72, 8},
+ {183, 8},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {186, 8},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {68, 6},
+ {122, 8},
+ {74, 6},
+ {92, 6},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {76, 6},
+ {94, 6},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {77, 7},
+ {95, 7},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {187, 9},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {77, 7},
+ {95, 7},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {176, 10},
+ {148, 6},
+ {188, 10},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {200, 10},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {191, 10},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {203, 10},
+ {90, 10},
+ {108, 10},
+ {69, 7},
+ {126, 10},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {114, 10},
+ {71, 7},
+ {132, 10},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {138, 10},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {206, 10},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {116, 10},
+ {72, 8},
+ {134, 10},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {140, 10},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {15, 10},
+ {122, 8},
+ {23, 10},
+ {39, 10},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {27, 10},
+ {43, 10},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {51, 10},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {29, 10},
+ {45, 10},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {53, 10},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {57, 10},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {142, 10},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {30, 10},
+ {46, 10},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {54, 10},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {58, 10},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {60, 10},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {148, 6},
+ {209, 12},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {209, 12},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {192, 11},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {204, 11},
+ {155, 7},
+ {167, 7},
+ {69, 7},
+ {179, 7},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {170, 7},
+ {71, 7},
+ {182, 7},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {185, 7},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {207, 11},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {117, 11},
+ {72, 8},
+ {135, 11},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {141, 11},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {68, 6},
+ {122, 8},
+ {74, 6},
+ {92, 6},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {76, 6},
+ {94, 6},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {77, 7},
+ {95, 7},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {143, 11},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {31, 11},
+ {47, 11},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {55, 11},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {59, 11},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {61, 11},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {176, 10},
+ {148, 6},
+ {188, 10},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {200, 10},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {191, 10},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {203, 10},
+ {90, 10},
+ {108, 10},
+ {69, 7},
+ {126, 10},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {114, 10},
+ {71, 7},
+ {132, 10},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {138, 10},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {206, 10},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {116, 10},
+ {72, 8},
+ {134, 10},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {140, 10},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {62, 11},
+ {15, 10},
+ {122, 8},
+ {23, 10},
+ {39, 10},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {27, 10},
+ {43, 10},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {51, 10},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {29, 10},
+ {45, 10},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {53, 10},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {57, 10},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {142, 10},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {30, 10},
+ {46, 10},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {54, 10},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {58, 10},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {60, 10},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {148, 6},
+ {209, 12},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {209, 12},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {209, 12},
+ {155, 7},
+ {167, 7},
+ {69, 7},
+ {179, 7},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {170, 7},
+ {71, 7},
+ {182, 7},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {185, 7},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {208, 12},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {171, 8},
+ {72, 8},
+ {183, 8},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {186, 8},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {68, 6},
+ {122, 8},
+ {74, 6},
+ {92, 6},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {76, 6},
+ {94, 6},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {77, 7},
+ {95, 7},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {144, 12},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {77, 7},
+ {95, 7},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {176, 10},
+ {148, 6},
+ {188, 10},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {200, 10},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {191, 10},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {203, 10},
+ {90, 10},
+ {108, 10},
+ {69, 7},
+ {126, 10},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {114, 10},
+ {71, 7},
+ {132, 10},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {138, 10},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {206, 10},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {116, 10},
+ {72, 8},
+ {134, 10},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {140, 10},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {63, 12},
+ {15, 10},
+ {122, 8},
+ {23, 10},
+ {39, 10},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {27, 10},
+ {43, 10},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {51, 10},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {29, 10},
+ {45, 10},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {53, 10},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {57, 10},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {142, 10},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {30, 10},
+ {46, 10},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {54, 10},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {58, 10},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {60, 10},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {148, 6},
+ {209, 12},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {209, 12},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {192, 11},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {204, 11},
+ {155, 7},
+ {167, 7},
+ {69, 7},
+ {179, 7},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {170, 7},
+ {71, 7},
+ {182, 7},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {185, 7},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {207, 11},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {117, 11},
+ {72, 8},
+ {135, 11},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {141, 11},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {104, 8},
+ {68, 6},
+ {122, 8},
+ {74, 6},
+ {92, 6},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {76, 6},
+ {94, 6},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {77, 7},
+ {95, 7},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {143, 11},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {31, 11},
+ {47, 11},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {55, 11},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {59, 11},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {61, 11},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {147, 5},
+ {209, 12},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {209, 12},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {176, 10},
+ {148, 6},
+ {188, 10},
+ {151, 6},
+ {163, 6},
+ {66, 6},
+ {200, 10},
+ {154, 6},
+ {166, 6},
+ {68, 6},
+ {178, 6},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {169, 6},
+ {70, 6},
+ {181, 6},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {191, 10},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {203, 10},
+ {90, 10},
+ {108, 10},
+ {69, 7},
+ {126, 10},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {114, 10},
+ {71, 7},
+ {132, 10},
+ {77, 7},
+ {95, 7},
+ {65, 5},
+ {194, 7},
+ {83, 7},
+ {101, 7},
+ {67, 5},
+ {119, 7},
+ {73, 5},
+ {91, 5},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {138, 10},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {103, 7},
+ {68, 6},
+ {121, 7},
+ {74, 6},
+ {92, 6},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {76, 6},
+ {94, 6},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {206, 10},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {116, 10},
+ {72, 8},
+ {134, 10},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {140, 10},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {62, 11},
+ {15, 10},
+ {122, 8},
+ {23, 10},
+ {39, 10},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {27, 10},
+ {43, 10},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {51, 10},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {29, 10},
+ {45, 10},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {53, 10},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {57, 10},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {146, 4},
+ {209, 12},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {160, 9},
+ {172, 9},
+ {147, 5},
+ {184, 9},
+ {150, 5},
+ {162, 5},
+ {65, 5},
+ {196, 9},
+ {153, 5},
+ {165, 5},
+ {67, 5},
+ {177, 5},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {175, 9},
+ {148, 6},
+ {142, 10},
+ {81, 9},
+ {99, 9},
+ {66, 6},
+ {199, 9},
+ {87, 9},
+ {105, 9},
+ {68, 6},
+ {123, 9},
+ {74, 6},
+ {92, 6},
+ {64, 4},
+ {209, 12},
+ {157, 6},
+ {111, 9},
+ {70, 6},
+ {129, 9},
+ {76, 6},
+ {94, 6},
+ {65, 5},
+ {193, 6},
+ {82, 6},
+ {100, 6},
+ {67, 5},
+ {118, 6},
+ {73, 5},
+ {91, 5},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {190, 9},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {202, 9},
+ {89, 9},
+ {107, 9},
+ {69, 7},
+ {125, 9},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {113, 9},
+ {71, 7},
+ {131, 9},
+ {30, 10},
+ {46, 10},
+ {7, 9},
+ {194, 7},
+ {83, 7},
+ {54, 10},
+ {11, 9},
+ {119, 7},
+ {19, 9},
+ {35, 9},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {137, 9},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {58, 10},
+ {13, 9},
+ {121, 7},
+ {21, 9},
+ {37, 9},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {25, 9},
+ {41, 9},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {49, 9},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {145, 3},
+ {205, 9},
+ {156, 8},
+ {168, 8},
+ {146, 4},
+ {180, 8},
+ {149, 4},
+ {161, 4},
+ {64, 4},
+ {209, 12},
+ {159, 8},
+ {115, 9},
+ {72, 8},
+ {133, 9},
+ {78, 8},
+ {96, 8},
+ {65, 5},
+ {195, 8},
+ {84, 8},
+ {102, 8},
+ {67, 5},
+ {120, 8},
+ {73, 5},
+ {91, 5},
+ {64, 4},
+ {209, 12},
+ {209, 12},
+ {174, 8},
+ {148, 6},
+ {139, 9},
+ {80, 8},
+ {98, 8},
+ {66, 6},
+ {198, 8},
+ {86, 8},
+ {60, 10},
+ {14, 9},
+ {122, 8},
+ {22, 9},
+ {38, 9},
+ {3, 8},
+ {209, 12},
+ {157, 6},
+ {110, 8},
+ {70, 6},
+ {128, 8},
+ {26, 9},
+ {42, 9},
+ {5, 8},
+ {193, 6},
+ {82, 6},
+ {50, 9},
+ {9, 8},
+ {118, 6},
+ {17, 8},
+ {33, 8},
+ {0, 6},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {209, 12},
+ {189, 8},
+ {152, 7},
+ {164, 7},
+ {145, 3},
+ {201, 8},
+ {88, 8},
+ {106, 8},
+ {69, 7},
+ {124, 8},
+ {75, 7},
+ {93, 7},
+ {64, 4},
+ {209, 12},
+ {158, 7},
+ {112, 8},
+ {71, 7},
+ {130, 8},
+ {28, 9},
+ {44, 9},
+ {6, 8},
+ {194, 7},
+ {83, 7},
+ {52, 9},
+ {10, 8},
+ {119, 7},
+ {18, 8},
+ {34, 8},
+ {1, 7},
+ {209, 12},
+ {209, 12},
+ {173, 7},
+ {148, 6},
+ {136, 8},
+ {79, 7},
+ {97, 7},
+ {66, 6},
+ {197, 7},
+ {85, 7},
+ {56, 9},
+ {12, 8},
+ {121, 7},
+ {20, 8},
+ {36, 8},
+ {2, 7},
+ {209, 12},
+ {157, 6},
+ {109, 7},
+ {70, 6},
+ {127, 7},
+ {24, 8},
+ {40, 8},
+ {4, 7},
+ {193, 6},
+ {82, 6},
+ {48, 8},
+ {8, 7},
+ {118, 6},
+ {16, 7},
+ {32, 7},
+ {0, 6}};
+} // utf8_to_utf16 namespace
+} // tables namespace
+} // unnamed namespace
+} // namespace simdutf
+
+#endif // SIMDUTF_UTF8_TO_UTF16_TABLES_H
+/* end file src/tables/utf8_to_utf16_tables.h */
+/* begin file src/tables/utf16_to_utf8_tables.h */
+// file generated by scripts/sse_convert_utf16_to_utf8.py
+#ifndef SIMDUTF_UTF16_TO_UTF8_TABLES_H
+#define SIMDUTF_UTF16_TO_UTF8_TABLES_H
+
+namespace simdutf {
+namespace {
+namespace tables {
+namespace utf16_to_utf8 {
+
+ // 1 byte for length, 16 bytes for mask
+ const uint8_t pack_1_2_utf8_bytes[256][17] = {
+ {16,1,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14},
+ {15,0,3,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80},
+ {15,1,0,3,2,5,4,7,6,8,11,10,13,12,15,14,0x80},
+ {14,0,3,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80},
+ {15,1,0,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80},
+ {14,0,2,5,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {14,1,0,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,2,5,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {15,1,0,3,2,5,4,7,6,9,8,10,13,12,15,14,0x80},
+ {14,0,3,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80},
+ {14,1,0,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80},
+ {13,0,2,5,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {15,1,0,3,2,4,7,6,9,8,11,10,13,12,15,14,0x80},
+ {14,0,3,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {14,1,0,3,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {14,1,0,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,2,4,7,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,4,7,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,4,7,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {15,1,0,3,2,5,4,7,6,9,8,11,10,12,15,14,0x80},
+ {14,0,3,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80},
+ {14,1,0,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80},
+ {13,0,2,5,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80},
+ {13,0,3,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,2,4,7,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,7,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {15,1,0,3,2,5,4,6,9,8,11,10,13,12,15,14,0x80},
+ {14,0,3,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {14,1,0,3,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {14,1,0,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,2,5,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80},
+ {13,0,3,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,2,4,6,9,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,6,8,11,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,6,9,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,8,10,13,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80},
+ {13,0,3,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,2,5,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,5,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80},
+ {12,0,3,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,6,9,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,8,11,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,9,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,4,6,8,10,12,15,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {15,1,0,3,2,5,4,7,6,9,8,11,10,13,12,14,0x80},
+ {14,0,3,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80},
+ {14,1,0,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80},
+ {13,0,2,5,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80},
+ {13,0,3,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,2,4,7,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,7,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80},
+ {13,0,3,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80},
+ {12,0,2,5,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,5,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80},
+ {12,0,3,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,7,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,7,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,7,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,4,7,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {14,1,0,3,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80},
+ {13,0,3,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {13,1,0,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,2,5,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,5,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80},
+ {12,0,3,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,4,6,9,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,8,11,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,9,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,4,6,8,10,13,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {13,1,0,3,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80},
+ {12,0,3,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,2,5,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,5,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,5,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,5,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {12,1,0,3,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80},
+ {11,0,3,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,2,4,6,9,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,4,6,8,11,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,1,0,3,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80},
+ {10,0,3,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,3,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,3,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,1,0,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,2,4,6,9,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,1,0,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,2,4,6,8,10,12,14,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}
+ };
+
+ // 1 byte for length, 16 bytes for mask
+ const uint8_t pack_1_2_3_utf8_bytes[256][17] = {
+ {12,2,3,1,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80},
+ {9,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,3,1,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80},
+ {10,0,6,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,2,3,1,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80},
+ {8,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,3,1,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,7,5,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,4,10,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,6,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,7,5,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,4,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,2,3,1,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80},
+ {8,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,3,1,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,6,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,7,5,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,4,11,9,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,7,5,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,4,8,14,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,6,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,7,5,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,4,10,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,6,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,2,3,1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,3,1,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {1,0,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,7,5,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,2,3,1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,3,1,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,0,4,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,6,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,7,5,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,4,11,9,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,6,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,2,3,1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,3,1,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,0,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,7,5,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,4,8,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {11,2,3,1,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80},
+ {8,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,3,1,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,0,6,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,7,5,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,4,10,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,6,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,7,5,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,4,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,7,5,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,4,11,9,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,6,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,7,5,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,4,8,15,13,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {10,2,3,1,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,3,1,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,0,6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,7,5,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,4,10,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,6,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,2,3,1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,3,1,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,0,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,7,5,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,4,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {9,2,3,1,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,3,1,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,0,6,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,7,5,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,4,11,9,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {8,2,3,1,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,3,1,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,0,6,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,2,3,1,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {2,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,3,1,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,0,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {7,2,3,1,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,3,1,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,0,7,5,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {6,2,3,1,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {3,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {5,3,1,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80},
+ {4,0,4,8,12,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80,0x80}
+ };
+
+} // utf16_to_utf8 namespace
+} // tables namespace
+} // unnamed namespace
+} // namespace simdutf
+
+#endif // SIMDUTF_UTF16_TO_UTF8_TABLES_H
+/* end file src/tables/utf16_to_utf8_tables.h */
+// End of tables.
+
+// The scalar routines should be included once.
+/* begin file src/scalar/ascii.h */
+#ifndef SIMDUTF_ASCII_H
+#define SIMDUTF_ASCII_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace ascii {
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+// Only used by the fallback kernel.
+inline simdutf_warn_unused bool validate(const char *buf, size_t len) noexcept {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ uint64_t pos = 0;
+ // process in blocks of 16 bytes when possible
+ for (;pos + 16 <= len; pos += 16) {
+ uint64_t v1;
+ std::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) != 0) { return false; }
+ }
+ // process the tail byte-by-byte
+ for (;pos < len; pos ++) {
+ if (data[pos] >= 0b10000000) { return false; }
+ }
+ return true;
+}
+#endif
+
+inline simdutf_warn_unused result validate_with_errors(const char *buf, size_t len) noexcept {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ // process in blocks of 16 bytes when possible
+ for (;pos + 16 <= len; pos += 16) {
+ uint64_t v1;
+ std::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ std::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) != 0) {
+ for (;pos < len; pos ++) {
+ if (data[pos] >= 0b10000000) { return result(error_code::TOO_LARGE, pos); }
+ }
+ }
+ }
+ // process the tail byte-by-byte
+ for (;pos < len; pos ++) {
+ if (data[pos] >= 0b10000000) { return result(error_code::TOO_LARGE, pos); }
+ }
+ return result(error_code::SUCCESS, pos);
+}
+
+} // ascii namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/ascii.h */
+/* begin file src/scalar/latin1.h */
+#ifndef SIMDUTF_LATIN1_H
+#define SIMDUTF_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace latin1 {
+
+inline size_t utf32_length_from_latin1(size_t len) {
+ // We are not BOM aware.
+ return len; // a utf32 unit will always represent 1 latin1 character
+}
+
+inline size_t utf8_length_from_latin1(const char *buf, size_t len) {
+ const uint8_t * c = reinterpret_cast<const uint8_t *>(buf);
+ size_t answer = 0;
+ for(size_t i = 0; i<len; i++) {
+ if((c[i]>>7)) { answer++; }
+ }
+ return answer + len;
+}
+
+inline size_t utf16_length_from_latin1(size_t len) {
+ return len;
+}
+
+} // utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/latin1.h */
+
+/* begin file src/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */
+#ifndef SIMDUTF_VALID_UTF32_TO_UTF8_H
+#define SIMDUTF_VALID_UTF32_TO_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_utf8 {
+
+#if SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_PPC64
+// only used by the fallback and POWER kernel
+inline size_t convert_valid(const char32_t* buf, size_t len, char* utf8_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 2 ASCII characters
+ if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0xFFFFFF80FFFFFF80) == 0) {
+ *utf8_output++ = char(buf[pos]);
+ *utf8_output++ = char(buf[pos+1]);
+ pos += 2;
+ continue;
+ }
+ }
+ uint32_t word = data[pos];
+ if((word & 0xFFFFFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xFFFFF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word & 0xFFFF0000)==0) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos ++;
+ }
+ }
+ return utf8_output - start;
+}
+#endif // SIMDUTF_IMPLEMENTATION_FALLBACK || SIMDUTF_IMPLEMENTATION_PPC64
+
+} // utf32_to_utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_utf8/valid_utf32_to_utf8.h */
+/* begin file src/scalar/utf32_to_utf8/utf32_to_utf8.h */
+#ifndef SIMDUTF_UTF32_TO_UTF8_H
+#define SIMDUTF_UTF32_TO_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_utf8 {
+
+inline size_t convert(const char32_t* buf, size_t len, char* utf8_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 2 ASCII characters
+ if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0xFFFFFF80FFFFFF80) == 0) {
+ *utf8_output++ = char(buf[pos]);
+ *utf8_output++ = char(buf[pos+1]);
+ pos += 2;
+ continue;
+ }
+ }
+ uint32_t word = data[pos];
+ if((word & 0xFFFFFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xFFFFF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word & 0xFFFF0000)==0) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ if (word >= 0xD800 && word <= 0xDFFF) { return 0; }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ if (word > 0x10FFFF) { return 0; }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos ++;
+ }
+ }
+ return utf8_output - start;
+}
+
+inline result convert_with_errors(const char32_t* buf, size_t len, char* utf8_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 2 ASCII characters
+ if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0xFFFFFF80FFFFFF80) == 0) {
+ *utf8_output++ = char(buf[pos]);
+ *utf8_output++ = char(buf[pos+1]);
+ pos += 2;
+ continue;
+ }
+ }
+ uint32_t word = data[pos];
+ if((word & 0xFFFFFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xFFFFF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word & 0xFFFF0000)==0) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ if (word >= 0xD800 && word <= 0xDFFF) { return result(error_code::SURROGATE, pos); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ if (word > 0x10FFFF) { return result(error_code::TOO_LARGE, pos); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos ++;
+ }
+ }
+ return result(error_code::SUCCESS, utf8_output - start);
+}
+
+} // utf32_to_utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_utf8/utf32_to_utf8.h */
+
+/* begin file src/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */
+#ifndef SIMDUTF_VALID_UTF32_TO_UTF16_H
+#define SIMDUTF_VALID_UTF32_TO_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_utf16 {
+
+template <endianness big_endian>
+inline size_t convert_valid(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ uint32_t word = data[pos];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word);
+ pos++;
+ } else {
+ // will generate a surrogate pair
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ pos++;
+ }
+ }
+ return utf16_output - start;
+}
+
+} // utf32_to_utf16 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_utf16/valid_utf32_to_utf16.h */
+/* begin file src/scalar/utf32_to_utf16/utf32_to_utf16.h */
+#ifndef SIMDUTF_UTF32_TO_UTF16_H
+#define SIMDUTF_UTF32_TO_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_utf16 {
+
+template <endianness big_endian>
+inline size_t convert(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ uint32_t word = data[pos];
+ if((word & 0xFFFF0000)==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return 0; }
+ // will not generate a surrogate pair
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return 0; }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ pos++;
+ }
+ return utf16_output - start;
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ uint32_t word = data[pos];
+ if((word & 0xFFFF0000)==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return result(error_code::SURROGATE, pos); }
+ // will not generate a surrogate pair
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(uint16_t(word))) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return result(error_code::TOO_LARGE, pos); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ pos++;
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+}
+
+} // utf32_to_utf16 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_utf16/utf32_to_utf16.h */
+
+/* begin file src/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */
+#ifndef SIMDUTF_VALID_UTF16_TO_UTF8_H
+#define SIMDUTF_VALID_UTF16_TO_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_utf8 {
+
+template <endianness big_endian>
+inline size_t convert_valid(const char16_t* buf, size_t len, char* utf8_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 4 ASCII characters
+ if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if (!match_system(big_endian)) { v = (v >> 8) | (v << (64 - 8)); }
+ if ((v & 0xFF80FF80FF80FF80) == 0) {
+ size_t final_pos = pos + 4;
+ while(pos < final_pos) {
+ *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word & 0xFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word &0xF800 ) != 0xD800) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(pos + 1 >= len) { return 0; } // minimal bound checking
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ pos += 2;
+ }
+ }
+ return utf8_output - start;
+}
+
+} // utf16_to_utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_utf8/valid_utf16_to_utf8.h */
+/* begin file src/scalar/utf16_to_utf8/utf16_to_utf8.h */
+#ifndef SIMDUTF_UTF16_TO_UTF8_H
+#define SIMDUTF_UTF16_TO_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_utf8 {
+
+template <endianness big_endian>
+inline size_t convert(const char16_t* buf, size_t len, char* utf8_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 8 bytes
+ if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if (!match_system(big_endian)) { v = (v >> 8) | (v << (64 - 8)); }
+ if ((v & 0xFF80FF80FF80FF80) == 0) {
+ size_t final_pos = pos + 4;
+ while(pos < final_pos) {
+ *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word & 0xFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word &0xF800 ) != 0xD800) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ if(pos + 1 >= len) { return 0; }
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return 0; }
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return 0; }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ pos += 2;
+ }
+ }
+ return utf8_output - start;
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char16_t* buf, size_t len, char* utf8_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 8 bytes
+ if (pos + 4 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if (!match_system(big_endian)) v = (v >> 8) | (v << (64 - 8));
+ if ((v & 0xFF80FF80FF80FF80) == 0) {
+ size_t final_pos = pos + 4;
+ while(pos < final_pos) {
+ *utf8_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(buf[pos])) : char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word & 0xFF80)==0) {
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(word);
+ pos++;
+ } else if((word & 0xF800)==0) {
+ // will generate two UTF-8 bytes
+ // we have 0b110XXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else if((word &0xF800 ) != 0xD800) {
+ // will generate three UTF-8 bytes
+ // we have 0b1110XXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); }
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ pos += 2;
+ }
+ }
+ return result(error_code::SUCCESS, utf8_output - start);
+}
+
+} // utf16_to_utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_utf8/utf16_to_utf8.h */
+
+/* begin file src/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */
+#ifndef SIMDUTF_VALID_UTF16_TO_UTF32_H
+#define SIMDUTF_VALID_UTF16_TO_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_utf32 {
+
+template <endianness big_endian>
+inline size_t convert_valid(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word &0xF800 ) != 0xD800) {
+ // No surrogate pair, extend 16-bit word to 32-bit word
+ *utf32_output++ = char32_t(word);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(pos + 1 >= len) { return 0; } // minimal bound checking
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ pos += 2;
+ }
+ }
+ return utf32_output - start;
+}
+
+} // utf16_to_utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_utf32/valid_utf16_to_utf32.h */
+/* begin file src/scalar/utf16_to_utf32/utf16_to_utf32.h */
+#ifndef SIMDUTF_UTF16_TO_UTF32_H
+#define SIMDUTF_UTF16_TO_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_utf32 {
+
+template <endianness big_endian>
+inline size_t convert(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word &0xF800 ) != 0xD800) {
+ // No surrogate pair, extend 16-bit word to 32-bit word
+ *utf32_output++ = char32_t(word);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return 0; }
+ if(pos + 1 >= len) { return 0; } // minimal bound checking
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return 0; }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ pos += 2;
+ }
+ }
+ return utf32_output - start;
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ uint16_t word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word &0xF800 ) != 0xD800) {
+ // No surrogate pair, extend 16-bit word to 32-bit word
+ *utf32_output++ = char32_t(word);
+ pos++;
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ if(diff > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ if(pos + 1 >= len) { return result(error_code::SURROGATE, pos); } // minimal bound checking
+ uint16_t next_word = !match_system(big_endian) ? utf16::swap_bytes(data[pos + 1]) : data[pos + 1];
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if(diff2 > 0x3FF) { return result(error_code::SURROGATE, pos); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ pos += 2;
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+}
+
+} // utf16_to_utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_utf32/utf16_to_utf32.h */
+
+/* begin file src/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */
+#ifndef SIMDUTF_VALID_UTF8_TO_UTF16_H
+#define SIMDUTF_VALID_UTF8_TO_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_utf16 {
+
+template <endianness big_endian>
+inline size_t convert_valid(const char* buf, size_t len, char16_t* utf16_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ // try to convert the next block of 8 ASCII bytes
+ if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 8;
+ while(pos < final_pos) {
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)) : char16_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 1 >= len) { break; } // minimal bound checking
+ uint16_t code_point = uint16_t(((leading_byte &0b00011111) << 6) | (data[pos + 1] &0b00111111));
+ if (!match_system(big_endian)) {
+ code_point = utf16::swap_bytes(uint16_t(code_point));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 2 >= len) { break; } // minimal bound checking
+ uint16_t code_point = uint16_t(((leading_byte &0b00001111) << 12) | ((data[pos + 1] &0b00111111) << 6) | (data[pos + 2] &0b00111111));
+ if (!match_system(big_endian)) {
+ code_point = utf16::swap_bytes(uint16_t(code_point));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { break; } // minimal bound checking
+ uint32_t code_point = ((leading_byte & 0b00000111) << 18 )| ((data[pos + 1] &0b00111111) << 12)
+ | ((data[pos + 2] &0b00111111) << 6) | (data[pos + 3] &0b00111111);
+ code_point -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ pos += 4;
+ } else {
+ // we may have a continuation but we do not do error checking
+ return 0;
+ }
+ }
+ return utf16_output - start;
+}
+
+
+} // namespace utf8_to_utf16
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_utf16/valid_utf8_to_utf16.h */
+/* begin file src/scalar/utf8_to_utf16/utf8_to_utf16.h */
+#ifndef SIMDUTF_UTF8_TO_UTF16_H
+#define SIMDUTF_UTF8_TO_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_utf16 {
+
+template <endianness big_endian>
+inline size_t convert(const char* buf, size_t len, char16_t* utf16_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)): char16_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 1 >= len) { return 0; } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if (code_point < 0x80 || 0x7ff < code_point) { return 0; }
+ if (!match_system(big_endian)) {
+ code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point)));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 2 >= len) { return 0; } // minimal bound checking
+
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if (code_point < 0x800 || 0xffff < code_point ||
+ (0xd7ff < code_point && code_point < 0xe000)) {
+ return 0;
+ }
+ if (!match_system(big_endian)) {
+ code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point)));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { return 0; } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return 0; }
+
+ // range check
+ uint32_t code_point =
+ (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff || 0x10ffff < code_point) { return 0; }
+ code_point -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ pos += 4;
+ } else {
+ return 0;
+ }
+ }
+ return utf16_output - start;
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char* buf, size_t len, char16_t* utf16_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(buf[pos])) : char16_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf16_output++ = !match_system(big_endian) ? char16_t(utf16::swap_bytes(leading_byte)): char16_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 1 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if (code_point < 0x80 || 0x7ff < code_point) { return result(error_code::OVERLONG, pos); }
+ if (!match_system(big_endian)) {
+ code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point)));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8, it should become
+ // a single UTF-16 word.
+ if(pos + 2 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if ((code_point < 0x800) || (0xffff < code_point)) { return result(error_code::OVERLONG, pos);}
+ if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); }
+ if (!match_system(big_endian)) {
+ code_point = uint32_t(utf16::swap_bytes(uint16_t(code_point)));
+ }
+ *utf16_output++ = char16_t(code_point);
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+
+ // range check
+ uint32_t code_point =
+ (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); }
+ if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); }
+ code_point -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (code_point >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (code_point & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = utf16::swap_bytes(high_surrogate);
+ low_surrogate = utf16::swap_bytes(low_surrogate);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ pos += 4;
+ } else {
+ // we either have too many continuation bytes or an invalid leading byte
+ if ((leading_byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); }
+ else { return result(error_code::HEADER_BITS, pos); }
+ }
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+}
+
+/**
+ * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and we have
+ * up to len input bytes left, and we encountered some error. It is possible that
+ * the error is at 'buf' exactly, but it could also be in the previous bytes (up to 3 bytes back).
+ *
+ * prior_bytes indicates how many bytes, prior to 'buf' may belong to the current memory section
+ * and can be safely accessed. We prior_bytes to access safely up to three bytes before 'buf'.
+ *
+ * The caller is responsible to ensure that len > 0.
+ *
+ * If the error is believed to have occurred prior to 'buf', the count value contain in the result
+ * will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3.
+ */
+template <endianness endian>
+inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char16_t* utf16_output) {
+ size_t extra_len{0};
+ // We potentially need to go back in time and find a leading byte.
+ // In theory '3' would be sufficient, but sometimes the error can go back quite far.
+ size_t how_far_back = prior_bytes;
+ // size_t how_far_back = 3; // 3 bytes in the past + current position
+ // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
+ bool found_leading_bytes{false};
+ // important: it is i <= how_far_back and not 'i < how_far_back'.
+ for(size_t i = 0; i <= how_far_back; i++) {
+ unsigned char byte = buf[0-i];
+ found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
+ if(found_leading_bytes) {
+ buf -= i;
+ extra_len = i;
+ break;
+ }
+ }
+ //
+ // It is possible for this function to return a negative count in its result.
+ // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described in C Standard as <stddef.h>.
+ // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator
+ //
+ // An unsigned type will simply wrap round arithmetically (well defined).
+ //
+ if(!found_leading_bytes) {
+ // If how_far_back == 3, we may have four consecutive continuation bytes!!!
+ // [....] [continuation] [continuation] [continuation] | [buf is continuation]
+ // Or we possibly have a stream that does not start with a leading byte.
+ return result(error_code::TOO_LONG, 0-how_far_back);
+ }
+ result res = convert_with_errors<endian>(buf, len + extra_len, utf16_output);
+ if (res.error) {
+ res.count -= extra_len;
+ }
+ return res;
+}
+
+} // utf8_to_utf16 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_utf16/utf8_to_utf16.h */
+
+/* begin file src/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */
+#ifndef SIMDUTF_VALID_UTF8_TO_UTF32_H
+#define SIMDUTF_VALID_UTF8_TO_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_utf32 {
+
+inline size_t convert_valid(const char* buf, size_t len, char32_t* utf32_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ // try to convert the next block of 8 ASCII bytes
+ if (pos + 8 <= len) { // if it is safe to read 8 more bytes, check that they are ascii
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 8;
+ while(pos < final_pos) {
+ *utf32_output++ = char32_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf32_output++ = char32_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) { break; } // minimal bound checking
+ *utf32_output++ = char32_t(((leading_byte &0b00011111) << 6) | (data[pos + 1] &0b00111111));
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8
+ if(pos + 2 >= len) { break; } // minimal bound checking
+ *utf32_output++ = char32_t(((leading_byte &0b00001111) << 12) | ((data[pos + 1] &0b00111111) << 6) | (data[pos + 2] &0b00111111));
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { break; } // minimal bound checking
+ uint32_t code_word = ((leading_byte & 0b00000111) << 18 )| ((data[pos + 1] &0b00111111) << 12)
+ | ((data[pos + 2] &0b00111111) << 6) | (data[pos + 3] &0b00111111);
+ *utf32_output++ = char32_t(code_word);
+ pos += 4;
+ } else {
+ // we may have a continuation but we do not do error checking
+ return 0;
+ }
+ }
+ return utf32_output - start;
+}
+
+
+} // namespace utf8_to_utf32
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_utf32/valid_utf8_to_utf32.h */
+/* begin file src/scalar/utf8_to_utf32/utf8_to_utf32.h */
+#ifndef SIMDUTF_UTF8_TO_UTF32_H
+#define SIMDUTF_UTF8_TO_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_utf32 {
+
+inline size_t convert(const char* buf, size_t len, char32_t* utf32_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *utf32_output++ = char32_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf32_output++ = char32_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) { return 0; } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if (code_point < 0x80 || 0x7ff < code_point) { return 0; }
+ *utf32_output++ = char32_t(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8
+ if(pos + 2 >= len) { return 0; } // minimal bound checking
+
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if (code_point < 0x800 || 0xffff < code_point ||
+ (0xd7ff < code_point && code_point < 0xe000)) {
+ return 0;
+ }
+ *utf32_output++ = char32_t(code_point);
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { return 0; } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return 0; }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return 0; }
+
+ // range check
+ uint32_t code_point =
+ (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff || 0x10ffff < code_point) { return 0; }
+ *utf32_output++ = char32_t(code_point);
+ pos += 4;
+ } else {
+ return 0;
+ }
+ }
+ return utf32_output - start;
+}
+
+inline result convert_with_errors(const char* buf, size_t len, char32_t* utf32_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2};
+ if ((v & 0x8080808080808080) == 0) {
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *utf32_output++ = char32_t(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *utf32_output++ = char32_t(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) {
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111);
+ if (code_point < 0x80 || 0x7ff < code_point) { return result(error_code::OVERLONG, pos); }
+ *utf32_output++ = char32_t(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8
+ if(pos + 2 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ // range check
+ uint32_t code_point = (leading_byte & 0b00001111) << 12 |
+ (data[pos + 1] & 0b00111111) << 6 |
+ (data[pos + 2] & 0b00111111);
+ if (code_point < 0x800 || 0xffff < code_point) { return result(error_code::OVERLONG, pos); }
+ if (0xd7ff < code_point && code_point < 0xe000) { return result(error_code::SURROGATE, pos); }
+ *utf32_output++ = char32_t(code_point);
+ pos += 3;
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ if(pos + 3 >= len) { return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos);}
+ if ((data[pos + 2] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+ if ((data[pos + 3] & 0b11000000) != 0b10000000) { return result(error_code::TOO_SHORT, pos); }
+
+ // range check
+ uint32_t code_point =
+ (leading_byte & 0b00000111) << 18 | (data[pos + 1] & 0b00111111) << 12 |
+ (data[pos + 2] & 0b00111111) << 6 | (data[pos + 3] & 0b00111111);
+ if (code_point <= 0xffff) { return result(error_code::OVERLONG, pos); }
+ if (0x10ffff < code_point) { return result(error_code::TOO_LARGE, pos); }
+ *utf32_output++ = char32_t(code_point);
+ pos += 4;
+ } else {
+ // we either have too many continuation bytes or an invalid leading byte
+ if ((leading_byte & 0b11000000) == 0b10000000) { return result(error_code::TOO_LONG, pos); }
+ else { return result(error_code::HEADER_BITS, pos); }
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+}
+
+/**
+ * When rewind_and_convert_with_errors is called, we are pointing at 'buf' and we have
+ * up to len input bytes left, and we encountered some error. It is possible that
+ * the error is at 'buf' exactly, but it could also be in the previous bytes location (up to 3 bytes back).
+ *
+ * prior_bytes indicates how many bytes, prior to 'buf' may belong to the current memory section
+ * and can be safely accessed. We prior_bytes to access safely up to three bytes before 'buf'.
+ *
+ * The caller is responsible to ensure that len > 0.
+ *
+ * If the error is believed to have occurred prior to 'buf', the count value contain in the result
+ * will be SIZE_T - 1, SIZE_T - 2, or SIZE_T - 3.
+ */
+inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char32_t* utf32_output) {
+ size_t extra_len{0};
+ // We potentially need to go back in time and find a leading byte.
+ size_t how_far_back = 3; // 3 bytes in the past + current position
+ if(how_far_back > prior_bytes) { how_far_back = prior_bytes; }
+ bool found_leading_bytes{false};
+ // important: it is i <= how_far_back and not 'i < how_far_back'.
+ for(size_t i = 0; i <= how_far_back; i++) {
+ unsigned char byte = buf[0-i];
+ found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
+ if(found_leading_bytes) {
+ buf -= i;
+ extra_len = i;
+ break;
+ }
+ }
+ //
+ // It is possible for this function to return a negative count in its result.
+ // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described in C Standard as <stddef.h>.
+ // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator
+ //
+ // An unsigned type will simply wrap round arithmetically (well defined).
+ //
+ if(!found_leading_bytes) {
+ // If how_far_back == 3, we may have four consecutive continuation bytes!!!
+ // [....] [continuation] [continuation] [continuation] | [buf is continuation]
+ // Or we possibly have a stream that does not start with a leading byte.
+ return result(error_code::TOO_LONG, 0-how_far_back);
+ }
+
+ result res = convert_with_errors(buf, len + extra_len, utf32_output);
+ if (res.error) {
+ res.count -= extra_len;
+ }
+ return res;
+}
+
+} // utf8_to_utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_utf32/utf8_to_utf32.h */
+
+/* begin file src/scalar/latin1_to_utf8/latin1_to_utf8.h */
+#ifndef SIMDUTF_LATIN1_TO_UTF8_H
+#define SIMDUTF_LATIN1_TO_UTF8_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace latin1_to_utf8 {
+
+inline size_t convert(const char* buf, size_t len, char* utf8_output) {
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
+ size_t pos = 0;
+ char* start{utf8_output};
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000, so it makes sense to concatenate everything
+ if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *utf8_output++ = char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+
+ unsigned char byte = data[pos];
+ if((byte & 0x80) == 0) { // if ASCII
+ // will generate one UTF-8 bytes
+ *utf8_output++ = char(byte);
+ pos++;
+ } else {
+ // will generate two UTF-8 bytes
+ *utf8_output++ = char((byte>>6) | 0b11000000);
+ *utf8_output++ = char((byte & 0b111111) | 0b10000000);
+ pos++;
+ }
+ }
+ return utf8_output - start;
+}
+
+} // latin1_to_utf8 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/latin1_to_utf8/latin1_to_utf8.h */
+/* begin file src/scalar/latin1_to_utf16/latin1_to_utf16.h */
+#ifndef SIMDUTF_LATIN1_TO_UTF16_H
+#define SIMDUTF_LATIN1_TO_UTF16_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace latin1_to_utf16 {
+
+template <endianness big_endian>
+inline size_t convert(const char* buf, size_t len, char16_t* utf16_output) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
+ size_t pos = 0;
+ char16_t* start{ utf16_output };
+
+ while (pos < len) {
+ uint16_t word = uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
+ *utf16_output++ = char16_t(match_system(big_endian) ? word : utf16::swap_bytes(word));
+ pos++;
+ }
+
+ return utf16_output - start;
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char* buf, size_t len, char16_t* utf16_output) {
+ const uint8_t* data = reinterpret_cast<const uint8_t*>(buf);
+ size_t pos = 0;
+ char16_t* start{ utf16_output };
+
+ while (pos < len) {
+ uint16_t word = uint16_t(data[pos]); // extend Latin-1 char to 16-bit Unicode code point
+ *utf16_output++ = char16_t(match_system(big_endian) ? word : utf16::swap_bytes(word));
+ pos++;
+ }
+
+ return result(error_code::SUCCESS, utf16_output - start);
+}
+
+} // latin1_to_utf16 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/latin1_to_utf16/latin1_to_utf16.h */
+/* begin file src/scalar/latin1_to_utf32/latin1_to_utf32.h */
+#ifndef SIMDUTF_LATIN1_TO_UTF32_H
+#define SIMDUTF_LATIN1_TO_UTF32_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace latin1_to_utf32 {
+
+
+inline size_t convert(const char *buf, size_t len, char32_t *utf32_output) {
+ const unsigned char *data = reinterpret_cast<const unsigned char *>(buf);
+ char32_t* start{utf32_output};
+ for (size_t i = 0; i < len; i++) {
+ *utf32_output++ = (char32_t)data[i];
+ }
+ return utf32_output - start;
+}
+
+} // latin1_to_utf32 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/latin1_to_utf32/latin1_to_utf32.h */
+
+/* begin file src/scalar/utf8_to_latin1/utf8_to_latin1.h */
+#ifndef SIMDUTF_UTF8_TO_LATIN1_H
+#define SIMDUTF_UTF8_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_latin1 {
+
+inline size_t convert(const char* buf, size_t len, char* latin_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char* start{latin_output};
+
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000 .... etc
+ if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *latin_output++ = char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+
+ // suppose it is not an all ASCII byte sequence
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *latin_output++ = char(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate:
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) {
+ return 0;
+ } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10.
+ // range check -
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation.
+ if (code_point < 0x80 || 0xFF < code_point) {
+ return 0; // We only care about the range 129-255 which is Non-ASCII latin1 characters. A code_point beneath 0x80 is invalid as it's already covered by bytes whose leading bit is zero.
+ }
+ *latin_output++ = char(code_point);
+ pos += 2;
+ } else {
+ return 0;
+ }
+ }
+ return latin_output - start;
+}
+
+inline result convert_with_errors(const char* buf, size_t len, char* latin_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+ size_t pos = 0;
+ char* start{latin_output};
+
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000...etc
+ if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *latin_output++ = char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ // suppose it is not an all ASCII byte sequence
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *latin_output++ = char(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate:
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) {
+ return result(error_code::TOO_SHORT, pos); } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) {
+ return result(error_code::TOO_SHORT, pos); } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10.
+ // range check -
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation.
+ if (code_point < 0x80) {
+ return result(error_code::OVERLONG, pos);
+ }
+ if (0xFF < code_point) {
+ return result(error_code::TOO_LARGE, pos);
+ } // We only care about the range 129-255 which is Non-ASCII latin1 characters
+ *latin_output++ = char(code_point);
+ pos += 2;
+ } else if ((leading_byte & 0b11110000) == 0b11100000) {
+ // We have a three-byte UTF-8
+ return result(error_code::TOO_LARGE, pos);
+ } else if ((leading_byte & 0b11111000) == 0b11110000) { // 0b11110000
+ // we have a 4-byte UTF-8 word.
+ return result(error_code::TOO_LARGE, pos);
+ } else {
+ // we either have too many continuation bytes or an invalid leading byte
+ if ((leading_byte & 0b11000000) == 0b10000000) {
+ return result(error_code::TOO_LONG, pos);
+ }
+
+ return result(error_code::HEADER_BITS, pos);
+
+ }
+ }
+ return result(error_code::SUCCESS, latin_output - start);
+}
+
+
+inline result rewind_and_convert_with_errors(size_t prior_bytes, const char* buf, size_t len, char* latin1_output) {
+ size_t extra_len{0};
+ // We potentially need to go back in time and find a leading byte.
+ // In theory '3' would be sufficient, but sometimes the error can go back quite far.
+ size_t how_far_back = prior_bytes;
+ // size_t how_far_back = 3; // 3 bytes in the past + current position
+ // if(how_far_back >= prior_bytes) { how_far_back = prior_bytes; }
+ bool found_leading_bytes{false};
+ // important: it is i <= how_far_back and not 'i < how_far_back'.
+ for(size_t i = 0; i <= how_far_back; i++) {
+ unsigned char byte = buf[0-i];
+ found_leading_bytes = ((byte & 0b11000000) != 0b10000000);
+ if(found_leading_bytes) {
+ buf -= i;
+ extra_len = i;
+ break;
+ }
+ }
+ //
+ // It is possible for this function to return a negative count in its result.
+ // C++ Standard Section 18.1 defines size_t is in <cstddef> which is described in C Standard as <stddef.h>.
+ // C Standard Section 4.1.5 defines size_t as an unsigned integral type of the result of the sizeof operator
+ //
+ // An unsigned type will simply wrap round arithmetically (well defined).
+ //
+ if(!found_leading_bytes) {
+ // If how_far_back == 3, we may have four consecutive continuation bytes!!!
+ // [....] [continuation] [continuation] [continuation] | [buf is continuation]
+ // Or we possibly have a stream that does not start with a leading byte.
+ return result(error_code::TOO_LONG, 0-how_far_back);
+ }
+ result res = convert_with_errors(buf, len + extra_len, latin1_output);
+ if (res.error) {
+ res.count -= extra_len;
+ }
+ return res;
+}
+
+
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_latin1/utf8_to_latin1.h */
+/* begin file src/scalar/utf16_to_latin1/utf16_to_latin1.h */
+#ifndef SIMDUTF_UTF16_TO_LATIN1_H
+#define SIMDUTF_UTF16_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_latin1 {
+
+#include <cstring> // for std::memcpy
+
+template <endianness big_endian>
+inline size_t convert(const char16_t* buf, size_t len, char* latin_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ std::vector<char> temp_output(len);
+ char* current_write = temp_output.data();
+ uint16_t word = 0;
+ uint16_t too_large = 0;
+
+ while (pos < len) {
+ word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ too_large |= word;
+ *current_write++ = char(word & 0xFF);
+ pos++;
+ }
+ if((too_large & 0xFF00) != 0) { return 0; }
+
+ // Only copy to latin_output if there were no errors
+ std::memcpy(latin_output, temp_output.data(), len);
+
+ return current_write - temp_output.data();
+}
+
+template <endianness big_endian>
+inline result convert_with_errors(const char16_t* buf, size_t len, char* latin_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char* start{latin_output};
+ uint16_t word;
+
+ while (pos < len) {
+ if (pos + 16 <= len) { // if it is safe to read 32 more bytes, check that they are Latin1
+ uint64_t v1, v2, v3, v4;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ ::memcpy(&v2, data + pos + 4, sizeof(uint64_t));
+ ::memcpy(&v3, data + pos + 8, sizeof(uint64_t));
+ ::memcpy(&v4, data + pos + 12, sizeof(uint64_t));
+
+ if (!match_system(big_endian)) { v1 = (v1 >> 8) | (v1 << (64 - 8)); }
+ if (!match_system(big_endian)) { v2 = (v2 >> 8) | (v2 << (64 - 8)); }
+ if (!match_system(big_endian)) { v3 = (v3 >> 8) | (v3 << (64 - 8)); }
+ if (!match_system(big_endian)) { v4 = (v1 >> 8) | (v4 << (64 - 8)); }
+
+ if (((v1 | v2 | v3 | v4) & 0xFF00FF00FF00FF00) == 0) {
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *latin_output++ = !match_system(big_endian) ? char(utf16::swap_bytes(data[pos])) : char(data[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+ word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ if((word & 0xFF00 ) == 0) {
+ *latin_output++ = char(word & 0xFF);
+ pos++;
+ } else { return result(error_code::TOO_LARGE, pos); }
+ }
+ return result(error_code::SUCCESS,latin_output - start);
+}
+
+
+} // utf16_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_latin1/utf16_to_latin1.h */
+/* begin file src/scalar/utf32_to_latin1/utf32_to_latin1.h */
+#ifndef SIMDUTF_UTF32_TO_LATIN1_H
+#define SIMDUTF_UTF32_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_latin1 {
+
+inline size_t convert(const char32_t *buf, size_t len, char *latin1_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ char* start = latin1_output;
+ uint32_t utf32_char;
+ size_t pos = 0;
+ uint32_t too_large = 0;
+
+ while (pos < len) {
+ utf32_char = (uint32_t)data[pos];
+ too_large |= utf32_char;
+ *latin1_output++ = (char)(utf32_char & 0xFF);
+ pos++;
+ }
+ if((too_large & 0xFFFFFF00) != 0) { return 0; }
+ return latin1_output - start;
+}
+
+inline result convert_with_errors(const char32_t *buf, size_t len, char *latin1_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ char* start{latin1_output};
+ size_t pos = 0;
+ while (pos < len) {
+ if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are Latin1
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0xFFFFFF00FFFFFF00) == 0) {
+ *latin1_output++ = char(buf[pos]);
+ *latin1_output++ = char(buf[pos+1]);
+ pos += 2;
+ continue;
+ }
+ }
+ uint32_t utf32_char = data[pos];
+ if ((utf32_char & 0xFFFFFF00) == 0) { // Check if the character can be represented in Latin-1
+ *latin1_output++ = (char)(utf32_char & 0xFF);
+ pos++;
+ } else { return result(error_code::TOO_LARGE, pos); };
+ }
+ return result(error_code::SUCCESS, latin1_output - start);
+}
+
+} // utf32_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_latin1/utf32_to_latin1.h */
+
+/* begin file src/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */
+#ifndef SIMDUTF_VALID_UTF8_TO_LATIN1_H
+#define SIMDUTF_VALID_UTF8_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf8_to_latin1 {
+
+inline size_t convert_valid(const char* buf, size_t len, char* latin_output) {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(buf);
+
+ size_t pos = 0;
+ char* start{latin_output};
+
+ while (pos < len) {
+ // try to convert the next block of 16 ASCII bytes
+ if (pos + 16 <= len) { // if it is safe to read 16 more bytes, check that they are ascii
+ uint64_t v1;
+ ::memcpy(&v1, data + pos, sizeof(uint64_t));
+ uint64_t v2;
+ ::memcpy(&v2, data + pos + sizeof(uint64_t), sizeof(uint64_t));
+ uint64_t v{v1 | v2}; // We are only interested in these bits: 1000 1000 1000 1000, so it makes sense to concatenate everything
+ if ((v & 0x8080808080808080) == 0) { // if NONE of these are set, e.g. all of them are zero, then everything is ASCII
+ size_t final_pos = pos + 16;
+ while(pos < final_pos) {
+ *latin_output++ = char(buf[pos]);
+ pos++;
+ }
+ continue;
+ }
+ }
+
+ // suppose it is not an all ASCII byte sequence
+ uint8_t leading_byte = data[pos]; // leading byte
+ if (leading_byte < 0b10000000) {
+ // converting one ASCII byte !!!
+ *latin_output++ = char(leading_byte);
+ pos++;
+ } else if ((leading_byte & 0b11100000) == 0b11000000) { // the first three bits indicate:
+ // We have a two-byte UTF-8
+ if(pos + 1 >= len) { break; } // minimal bound checking
+ if ((data[pos + 1] & 0b11000000) != 0b10000000) { return 0; } // checks if the next byte is a valid continuation byte in UTF-8. A valid continuation byte starts with 10.
+ // range check -
+ uint32_t code_point = (leading_byte & 0b00011111) << 6 | (data[pos + 1] & 0b00111111); // assembles the Unicode code point from the two bytes. It does this by discarding the leading 110 and 10 bits from the two bytes, shifting the remaining bits of the first byte, and then combining the results with a bitwise OR operation.
+ *latin_output++ = char(code_point);
+ pos += 2;
+ } else {
+ // we may have a continuation but we do not do error checking
+ return 0;
+ }
+ }
+ return latin_output - start;
+}
+
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf8_to_latin1/valid_utf8_to_latin1.h */
+/* begin file src/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */
+#ifndef SIMDUTF_VALID_UTF16_TO_LATIN1_H
+#define SIMDUTF_VALID_UTF16_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf16_to_latin1 {
+
+template <endianness big_endian>
+inline size_t convert_valid(const char16_t* buf, size_t len, char* latin_output) {
+ const uint16_t *data = reinterpret_cast<const uint16_t *>(buf);
+ size_t pos = 0;
+ char* start{latin_output};
+ uint16_t word = 0;
+
+ while (pos < len) {
+ word = !match_system(big_endian) ? utf16::swap_bytes(data[pos]) : data[pos];
+ *latin_output++ = char(word);
+ pos++;
+ }
+
+ return latin_output - start;
+}
+
+} // utf16_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf16_to_latin1/valid_utf16_to_latin1.h */
+/* begin file src/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */
+#ifndef SIMDUTF_VALID_UTF32_TO_LATIN1_H
+#define SIMDUTF_VALID_UTF32_TO_LATIN1_H
+
+namespace simdutf {
+namespace scalar {
+namespace {
+namespace utf32_to_latin1 {
+
+inline size_t convert_valid(const char32_t *buf, size_t len, char *latin1_output) {
+ const uint32_t *data = reinterpret_cast<const uint32_t *>(buf);
+ char* start = latin1_output;
+ uint32_t utf32_char;
+ size_t pos = 0;
+
+ while (pos < len) {
+ utf32_char = (uint32_t)data[pos];
+
+ if (pos + 2 <= len) { // if it is safe to read 8 more bytes, check that they are Latin1
+ uint64_t v;
+ ::memcpy(&v, data + pos, sizeof(uint64_t));
+ if ((v & 0xFFFFFF00FFFFFF00) == 0) {
+ *latin1_output++ = char(buf[pos]);
+ *latin1_output++ = char(buf[pos+1]);
+ pos += 2;
+ continue;
+ }
+ }
+ *latin1_output++ = (char)(utf32_char & 0xFF);
+ pos++;
+
+ }
+ return latin1_output - start;
+}
+
+
+} // utf32_to_latin1 namespace
+} // unnamed namespace
+} // namespace scalar
+} // namespace simdutf
+
+#endif
+/* end file src/scalar/utf32_to_latin1/valid_utf32_to_latin1.h */
+
+
+
+SIMDUTF_PUSH_DISABLE_WARNINGS
+SIMDUTF_DISABLE_UNDESIRED_WARNINGS
+
+
+#if SIMDUTF_IMPLEMENTATION_ARM64
+/* begin file src/arm64/implementation.cpp */
+/* begin file src/simdutf/arm64/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "arm64"
+// #define SIMDUTF_IMPLEMENTATION arm64
+/* end file src/simdutf/arm64/begin.h */
+namespace simdutf {
+namespace arm64 {
+namespace {
+#ifndef SIMDUTF_ARM64_H
+#error "arm64.h must be included"
+#endif
+using namespace simd;
+
+simdutf_really_inline bool is_ascii(const simd8x64<uint8_t>& input) {
+ simd8<uint8_t> bits = input.reduce_or();
+ return bits.max_val() < 0b10000000u;
+}
+
+simdutf_unused simdutf_really_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<bool> is_second_byte = prev1 >= uint8_t(0b11000000u);
+ simd8<bool> is_third_byte = prev2 >= uint8_t(0b11100000u);
+ simd8<bool> is_fourth_byte = prev3 >= uint8_t(0b11110000u);
+ // Use ^ instead of | for is_*_byte, because ^ is commutative, and the caller is using ^ as well.
+ // This will work fine because we only have to report errors for cases with 0-1 lead bytes.
+ // Multiple lead bytes implies 2 overlapping multibyte characters, and if that happens, there is
+ // guaranteed to be at least *one* lead byte that is part of only 1 other multibyte character.
+ // The error will be detected there.
+ return is_second_byte ^ is_third_byte ^ is_fourth_byte;
+}
+
+simdutf_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<bool> is_third_byte = prev2 >= uint8_t(0b11100000u);
+ simd8<bool> is_fourth_byte = prev3 >= uint8_t(0b11110000u);
+ return is_third_byte ^ is_fourth_byte;
+}
+
+// common functions for utf8 conversions
+simdutf_really_inline uint16x4_t convert_utf8_3_byte_to_utf16(uint8x16_t in) {
+ // Low half contains 10cccccc|1110aaaa
+ // High half contains 10bbbbbb|10bbbbbb
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t sh = simdutf_make_uint8x16_t(0, 2, 3, 5, 6, 8, 9, 11, 1, 1, 4, 4, 7, 7, 10, 10);
+#else
+ const uint8x16_t sh = {0, 2, 3, 5, 6, 8, 9, 11, 1, 1, 4, 4, 7, 7, 10, 10};
+#endif
+ uint8x16_t perm = vqtbl1q_u8(in, sh);
+ // Split into half vectors.
+ // 10cccccc|1110aaaa
+ uint8x8_t perm_low = vget_low_u8(perm); // no-op
+ // 10bbbbbb|10bbbbbb
+ uint8x8_t perm_high = vget_high_u8(perm);
+ // xxxxxxxx 10bbbbbb
+ uint16x4_t mid = vreinterpret_u16_u8(perm_high); // no-op
+ // xxxxxxxx 1110aaaa
+ uint16x4_t high = vreinterpret_u16_u8(perm_low); // no-op
+ // Assemble with shift left insert.
+ // xxxxxxaa aabbbbbb
+ uint16x4_t mid_high = vsli_n_u16(mid, high, 6);
+ // (perm_low << 8) | (perm_low >> 8)
+ // xxxxxxxx 10cccccc
+ uint16x4_t low = vreinterpret_u16_u8(vrev16_u8(perm_low));
+ // Shift left insert into the low bits
+ // aaaabbbb bbcccccc
+ uint16x4_t composed = vsli_n_u16(low, mid_high, 6);
+ return composed;
+}
+
+simdutf_really_inline uint16x8_t convert_utf8_2_byte_to_utf16(uint8x16_t in) {
+ // Converts 6 2 byte UTF-8 characters to 6 UTF-16 characters.
+ // Technically this calculates 8, but 6 does better and happens more often
+ // (The languages which use these codepoints use ASCII spaces so 8 would need to be
+ // in the middle of a very long word).
+
+ // 10bbbbbb 110aaaaa
+ uint16x8_t upper = vreinterpretq_u16_u8(in);
+ // (in << 8) | (in >> 8)
+ // 110aaaaa 10bbbbbb
+ uint16x8_t lower = vreinterpretq_u16_u8(vrev16q_u8(in));
+ // 00000000 000aaaaa
+ uint16x8_t upper_masked = vandq_u16(upper, vmovq_n_u16(0x1F));
+ // Assemble with shift left insert.
+ // 00000aaa aabbbbbb
+ uint16x8_t composed = vsliq_n_u16(lower, upper_masked, 6);
+ return composed;
+}
+
+simdutf_really_inline uint16x8_t convert_utf8_1_to_2_byte_to_utf16(uint8x16_t in, size_t shufutf8_idx) {
+ // Converts 6 1-2 byte UTF-8 characters to 6 UTF-16 characters.
+ // This is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes.
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[shufutf8_idx]));
+ // Shuffle
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 110aaaaa 10bbbbbb
+ uint16x8_t perm = vreinterpretq_u16_u8(vqtbl1q_u8(in, sh));
+ // Mask
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 00000000 00bbbbbb
+ uint16x8_t ascii = vandq_u16(perm, vmovq_n_u16(0x7f)); // 6 or 7 bits
+ // 1 byte: 00000000 00000000
+ // 2 byte: 000aaaaa 00000000
+ uint16x8_t highbyte = vandq_u16(perm, vmovq_n_u16(0x1f00)); // 5 bits
+ // Combine with a shift right accumulate
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 00000aaa aabbbbbb
+ uint16x8_t composed = vsraq_n_u16(ascii, highbyte, 2);
+ return composed;
+}
+
+/* begin file src/arm64/arm_detect_encodings.cpp */
+template<class checker>
+// len is known to be a multiple of 2 when this is called
+int arm_detect_encodings(const char * buf, size_t len) {
+ const char* start = buf;
+ const char* end = buf + len;
+
+ bool is_utf8 = true;
+ bool is_utf16 = true;
+ bool is_utf32 = true;
+
+ int out = 0;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+
+ uint32x4_t currentmax = vmovq_n_u32(0x0);
+
+ checker check{};
+
+ while(buf + 64 <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t*>(buf));
+ uint16x8_t secondin = vld1q_u16(reinterpret_cast<const uint16_t*>(buf) + simd16<uint16_t>::SIZE / sizeof(char16_t));
+ uint16x8_t thirdin = vld1q_u16(reinterpret_cast<const uint16_t*>(buf) + 2*simd16<uint16_t>::SIZE / sizeof(char16_t));
+ uint16x8_t fourthin = vld1q_u16(reinterpret_cast<const uint16_t*>(buf) + 3*simd16<uint16_t>::SIZE / sizeof(char16_t));
+
+ const auto u0 = simd16<uint16_t>(in);
+ const auto u1 = simd16<uint16_t>(secondin);
+ const auto u2 = simd16<uint16_t>(thirdin);
+ const auto u3 = simd16<uint16_t>(fourthin);
+
+ const auto v0 = u0.shr<8>();
+ const auto v1 = u1.shr<8>();
+ const auto v2 = u2.shr<8>();
+ const auto v3 = u3.shr<8>();
+
+ const auto in16 = simd16<uint16_t>::pack(v0, v1);
+ const auto nextin16 = simd16<uint16_t>::pack(v2, v3);
+
+ const uint64_t surrogates_wordmask0 = ((in16 & v_f8) == v_d8).to_bitmask64();
+ const uint64_t surrogates_wordmask1 = ((nextin16 & v_f8) == v_d8).to_bitmask64();
+
+ // Check for surrogates
+ if (surrogates_wordmask0 != 0 || surrogates_wordmask1 != 0) {
+ // Cannot be UTF8
+ is_utf8 = false;
+ // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates
+ // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word.
+ // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant
+ // bytes of a 32-bit word since they always come in pairs in UTF-16LE.
+ // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units.
+
+ if (((surrogates_wordmask0 | surrogates_wordmask1) & 0xf0f0f0f0f0f0f0f0) != 0) {
+ is_utf32 = false;
+ // Code from arm_validate_utf16le.cpp
+ // Not efficient, we do not process surrogates_wordmask1
+ const char16_t * input = reinterpret_cast<const char16_t*>(buf);
+ const char16_t* end16 = reinterpret_cast<const char16_t*>(start) + len/2;
+
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ const uint64_t V0 = ~surrogates_wordmask0;
+
+ const auto vH0 = ((in16 & v_fc) == v_dc);
+ const uint64_t H0 = vH0.to_bitmask64();
+
+ const uint64_t L0 = ~H0 & surrogates_wordmask0;
+
+ const uint64_t a0 = L0 & (H0 >> 4);
+
+ const uint64_t b0 = a0 << 4;
+
+ const uint64_t c0 = V0 | a0 | b0;
+ if (c0 == ~0ull) {
+ input += 16;
+ } else if (c0 == 0xfffffffffffffffull) {
+ input += 15;
+ } else {
+ is_utf16 = false;
+ break;
+ }
+
+ while (input + 16 < end16) {
+ const auto in0 = simd16<uint16_t>(input);
+ const auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+ const simd8<uint8_t> in_16 = simd16<uint16_t>::pack(t0, t1);
+
+ const uint64_t surrogates_wordmask = ((in_16 & v_f8) == v_d8).to_bitmask64();
+ if(surrogates_wordmask == 0) {
+ input += 16;
+ } else {
+ const uint64_t V = ~surrogates_wordmask;
+
+ const auto vH = ((in_16 & v_fc) == v_dc);
+ const uint64_t H = vH.to_bitmask64();
+
+ const uint64_t L = ~H & surrogates_wordmask;
+
+ const uint64_t a = L & (H >> 4);
+
+ const uint64_t b = a << 4;
+
+ const uint64_t c = V | a | b;
+ if (c == ~0ull) {
+ input += 16;
+ } else if (c == 0xfffffffffffffffull) {
+ input += 15;
+ } else {
+ is_utf16 = false;
+ break;
+ }
+ }
+ }
+ } else {
+ is_utf16 = false;
+ // Check for UTF-32
+ if (len % 4 == 0) {
+ const char32_t * input = reinterpret_cast<const char32_t*>(buf);
+ const char32_t* end32 = reinterpret_cast<const char32_t*>(start) + len/4;
+
+ // Must start checking for surrogates
+ uint32x4_t currentoffsetmax = vmovq_n_u32(0x0);
+ const uint32x4_t offset = vmovq_n_u32(0xffff2000);
+ const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff);
+
+ const uint32x4_t in32 = vreinterpretq_u32_u16(in);
+ const uint32x4_t secondin32 = vreinterpretq_u32_u16(secondin);
+ const uint32x4_t thirdin32 = vreinterpretq_u32_u16(thirdin);
+ const uint32x4_t fourthin32 = vreinterpretq_u32_u16(fourthin);
+
+ currentmax = vmaxq_u32(in32,currentmax);
+ currentmax = vmaxq_u32(secondin32,currentmax);
+ currentmax = vmaxq_u32(thirdin32,currentmax);
+ currentmax = vmaxq_u32(fourthin32,currentmax);
+
+ currentoffsetmax = vmaxq_u32(vaddq_u32(in32, offset), currentoffsetmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(secondin32, offset), currentoffsetmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(thirdin32, offset), currentoffsetmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(fourthin32, offset), currentoffsetmax);
+
+ while (input + 4 < end32) {
+ const uint32x4_t in_32 = vld1q_u32(reinterpret_cast<const uint32_t*>(input));
+ currentmax = vmaxq_u32(in_32,currentmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(in_32, offset), currentoffsetmax);
+ input += 4;
+ }
+
+ uint32x4_t forbidden_words = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(vmaxvq_u32(forbidden_words) != 0) {
+ is_utf32 = false;
+ }
+ } else {
+ is_utf32 = false;
+ }
+ }
+ break;
+ }
+ // If no surrogate, validate under other encodings as well
+
+ // UTF-32 validation
+ currentmax = vmaxq_u32(vreinterpretq_u32_u16(in),currentmax);
+ currentmax = vmaxq_u32(vreinterpretq_u32_u16(secondin),currentmax);
+ currentmax = vmaxq_u32(vreinterpretq_u32_u16(thirdin),currentmax);
+ currentmax = vmaxq_u32(vreinterpretq_u32_u16(fourthin),currentmax);
+
+ // UTF-8 validation
+ // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h
+ simd::simd8x64<uint8_t> in8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(secondin), vreinterpretq_u8_u16(thirdin), vreinterpretq_u8_u16(fourthin));
+ check.check_next_input(in8);
+
+ buf += 64;
+ }
+
+ // Check which encodings are possible
+
+ if (is_utf8) {
+ if (static_cast<size_t>(buf - start) != len) {
+ uint8_t block[64]{};
+ std::memset(block, 0x20, 64);
+ std::memcpy(block, buf, len - (buf - start));
+ simd::simd8x64<uint8_t> in(block);
+ check.check_next_input(in);
+ }
+ if (!check.errors()) {
+ out |= simdutf::encoding_type::UTF8;
+ }
+ }
+
+ if (is_utf16 && scalar::utf16::validate<endianness::LITTLE>(reinterpret_cast<const char16_t*>(buf), (len - (buf - start))/2)) {
+ out |= simdutf::encoding_type::UTF16_LE;
+ }
+
+ if (is_utf32 && (len % 4 == 0)) {
+ const uint32x4_t standardmax = vmovq_n_u32(0x10ffff);
+ uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax);
+ if (vmaxvq_u32(is_zero) == 0 && scalar::utf32::validate(reinterpret_cast<const char32_t*>(buf), (len - (buf - start))/4)) {
+ out |= simdutf::encoding_type::UTF32_LE;
+ }
+ }
+
+ return out;
+}
+/* end file src/arm64/arm_detect_encodings.cpp */
+
+/* begin file src/arm64/arm_validate_utf16.cpp */
+template <endianness big_endian>
+const char16_t* arm_validate_utf16(const char16_t* input, size_t size) {
+ const char16_t* end = input + size;
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+ while (input + 16 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+ if (!match_system(big_endian)) {
+ in0 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in0)));
+ in1 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in1)));
+ }
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+ const simd8<uint8_t> in = simd16<uint16_t>::pack(t0, t1);
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const uint64_t surrogates_wordmask = ((in & v_f8) == v_d8).to_bitmask64();
+ if(surrogates_wordmask == 0) {
+ input += 16;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint64_t V = ~surrogates_wordmask;
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = ((in & v_fc) == v_dc);
+ const uint64_t H = vH.to_bitmask64();
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint64_t L = ~H & surrogates_wordmask;
+
+ const uint64_t a = L & (H >> 4); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint64_t b = a << 4; // Just mark that the opposite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint64_t c = V | a | b; // Combine all the masks into the final one.
+ if (c == ~0ull) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += 16;
+ } else if (c == 0xfffffffffffffffull) {
+ // The 15 lower code units of the input register contains valid UTF-16.
+ // The 15th word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += 15;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+ return input;
+}
+
+
+template <endianness big_endian>
+const result arm_validate_utf16_with_errors(const char16_t* input, size_t size) {
+ const char16_t* start = input;
+ const char16_t* end = input + size;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+ while (input + 16 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+
+ if (!match_system(big_endian)) {
+ in0 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in0)));
+ in1 = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in1)));
+ }
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+ const simd8<uint8_t> in = simd16<uint16_t>::pack(t0, t1);
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const uint64_t surrogates_wordmask = ((in & v_f8) == v_d8).to_bitmask64();
+ if(surrogates_wordmask == 0) {
+ input += 16;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint64_t V = ~surrogates_wordmask;
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = ((in & v_fc) == v_dc);
+ const uint64_t H = vH.to_bitmask64();
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint64_t L = ~H & surrogates_wordmask;
+
+ const uint64_t a = L & (H >> 4); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint64_t b = a << 4; // Just mark that the opposite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint64_t c = V | a | b; // Combine all the masks into the final one.
+ if (c == ~0ull) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += 16;
+ } else if (c == 0xfffffffffffffffull) {
+ // The 15 lower code units of the input register contains valid UTF-16.
+ // The 15th word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += 15;
+ } else {
+ return result(error_code::SURROGATE, input - start);
+ }
+ }
+ }
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/arm64/arm_validate_utf16.cpp */
+/* begin file src/arm64/arm_validate_utf32le.cpp */
+
+const char32_t* arm_validate_utf32le(const char32_t* input, size_t size) {
+ const char32_t* end = input + size;
+
+ const uint32x4_t standardmax = vmovq_n_u32(0x10ffff);
+ const uint32x4_t offset = vmovq_n_u32(0xffff2000);
+ const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff);
+ uint32x4_t currentmax = vmovq_n_u32(0x0);
+ uint32x4_t currentoffsetmax = vmovq_n_u32(0x0);
+
+ while (input + 4 < end) {
+ const uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t*>(input));
+ currentmax = vmaxq_u32(in,currentmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(in, offset), currentoffsetmax);
+ input += 4;
+ }
+
+ uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax);
+ if(vmaxvq_u32(is_zero) != 0) {
+ return nullptr;
+ }
+
+ is_zero = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(vmaxvq_u32(is_zero) != 0) {
+ return nullptr;
+ }
+
+ return input;
+}
+
+
+const result arm_validate_utf32le_with_errors(const char32_t* input, size_t size) {
+ const char32_t* start = input;
+ const char32_t* end = input + size;
+
+ const uint32x4_t standardmax = vmovq_n_u32(0x10ffff);
+ const uint32x4_t offset = vmovq_n_u32(0xffff2000);
+ const uint32x4_t standardoffsetmax = vmovq_n_u32(0xfffff7ff);
+ uint32x4_t currentmax = vmovq_n_u32(0x0);
+ uint32x4_t currentoffsetmax = vmovq_n_u32(0x0);
+
+ while (input + 4 < end) {
+ const uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t*>(input));
+ currentmax = vmaxq_u32(in,currentmax);
+ currentoffsetmax = vmaxq_u32(vaddq_u32(in, offset), currentoffsetmax);
+
+ uint32x4_t is_zero = veorq_u32(vmaxq_u32(currentmax, standardmax), standardmax);
+ if(vmaxvq_u32(is_zero) != 0) {
+ return result(error_code::TOO_LARGE, input - start);
+ }
+
+ is_zero = veorq_u32(vmaxq_u32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(vmaxvq_u32(is_zero) != 0) {
+ return result(error_code::SURROGATE, input - start);
+ }
+
+ input += 4;
+ }
+
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/arm64/arm_validate_utf32le.cpp */
+
+/* begin file src/arm64/arm_convert_latin1_to_utf8.cpp */
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+std::pair<const char *, char *>
+arm_convert_latin1_to_utf8(const char *latin1_input, size_t len,
+ char *utf8_out) {
+ uint8_t *utf8_output = reinterpret_cast<uint8_t *>(utf8_out);
+ const char *end = latin1_input + len;
+ const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080);
+ // We always write 16 bytes, of which more than the first 8 bytes
+ // are valid. A safety margin of 8 is more than sufficient.
+ while (latin1_input + 16 + 8 <= end) {
+ uint8x16_t in8 = vld1q_u8(reinterpret_cast<const uint8_t *>(latin1_input));
+ if (vmaxvq_u8(in8) <= 0x7F) { // ASCII fast path!!!!
+ vst1q_u8(utf8_output, in8);
+ utf8_output += 16;
+ latin1_input += 16;
+ continue;
+ }
+
+ // We just fallback on UTF-16 code. This could be optimized/simplified
+ // further.
+ uint16x8_t in16 = vmovl_u8(vget_low_u8(in8));
+ // 1. prepare 2-byte values
+ // input 8-bit word : [aabb|bbbb] x 8
+ // expected output : [1100|00aa|10bb|bbbb] x 8
+ const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00);
+ const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f);
+
+ // t0 = [0000|00aa|bbbb|bb00]
+ const uint16x8_t t0 = vshlq_n_u16(in16, 2);
+ // t1 = [0000|00aa|0000|0000]
+ const uint16x8_t t1 = vandq_u16(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const uint16x8_t t2 = vandq_u16(in16, v_003f);
+ // t3 = [0000|00aa|00bb|bbbb]
+ const uint16x8_t t3 = vorrq_u16(t1, t2);
+ // t4 = [1100|00aa|10bb|bbbb]
+ const uint16x8_t t4 = vorrq_u16(t3, v_c080);
+ // 2. merge ASCII and 2-byte codewords
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(in16, v_007f);
+ const uint8x16_t utf8_unpacked =
+ vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in16, t4));
+ // 3. prepare bitmask for 8-bit lookup
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004, 0x0010, 0x0040,
+ 0x0002, 0x0008, 0x0020, 0x0080);
+#else
+ const uint16x8_t mask = {0x0001, 0x0004, 0x0010, 0x0040,
+ 0x0002, 0x0008, 0x0020, 0x0080};
+#endif
+ uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask));
+ // 4. pack the bytes
+ const uint8_t *row =
+ &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const uint8x16_t shuffle = vld1q_u8(row + 1);
+ const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ vst1q_u8(utf8_output, utf8_packed);
+ // 6. adjust pointers
+ latin1_input += 8;
+ utf8_output += row[0];
+
+ } // while
+
+ return std::make_pair(latin1_input, reinterpret_cast<char *>(utf8_output));
+}
+/* end file src/arm64/arm_convert_latin1_to_utf8.cpp */
+/* begin file src/arm64/arm_convert_latin1_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char*, char16_t*> arm_convert_latin1_to_utf16(const char* buf, size_t len, char16_t* utf16_output) {
+ const char* end = buf + len;
+
+ while (buf + 16 <= end) {
+ uint8x16_t in8 = vld1q_u8(reinterpret_cast<const uint8_t *>(buf));
+ uint16x8_t inlow = vmovl_u8(vget_low_u8(in8));
+ if (!match_system(big_endian)) { inlow = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(inlow))); }
+ vst1q_u16(reinterpret_cast<uint16_t *>(utf16_output), inlow);
+ uint16x8_t inhigh = vmovl_u8(vget_high_u8(in8));
+ if (!match_system(big_endian)) { inhigh = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(inhigh))); }
+ vst1q_u16(reinterpret_cast<uint16_t *>(utf16_output+8), inhigh);
+ utf16_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(buf, utf16_output);
+}
+/* end file src/arm64/arm_convert_latin1_to_utf16.cpp */
+/* begin file src/arm64/arm_convert_latin1_to_utf32.cpp */
+std::pair<const char*, char32_t*> arm_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
+ const char* end = buf + len;
+
+ while (buf + 16 <= end) {
+ uint8x16_t in8 = vld1q_u8(reinterpret_cast<const uint8_t *>(buf));
+ uint16x8_t in8low = vmovl_u8(vget_low_u8(in8));
+ uint32x4_t in16lowlow = vmovl_u16(vget_low_u16(in8low));
+ uint32x4_t in16lowhigh = vmovl_u16(vget_high_u16(in8low));
+ uint16x8_t in8high = vmovl_u8(vget_high_u8(in8));
+ uint32x4_t in8highlow = vmovl_u16(vget_low_u16(in8high));
+ uint32x4_t in8highhigh = vmovl_u16(vget_high_u16(in8high));
+ vst1q_u32(reinterpret_cast<uint32_t *>(utf32_output), in16lowlow);
+ vst1q_u32(reinterpret_cast<uint32_t *>(utf32_output+4), in16lowhigh);
+ vst1q_u32(reinterpret_cast<uint32_t *>(utf32_output+8), in8highlow);
+ vst1q_u32(reinterpret_cast<uint32_t *>(utf32_output+12), in8highhigh);
+
+ utf32_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(buf, utf32_output);
+}
+/* end file src/arm64/arm_convert_latin1_to_utf32.cpp */
+
+/* begin file src/arm64/arm_convert_utf8_to_utf16.cpp */
+// Convert up to 16 bytes from utf8 to utf16 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 16, usually 12).
+template <endianness big_endian>
+size_t convert_masked_utf8_to_utf16(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char16_t *&utf16_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ uint8x16_t in = vld1q_u8(reinterpret_cast<const uint8_t*>(input));
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+
+ // We first try a few fast paths.
+ // The obvious first test is ASCII, which actually consumes the full 16.
+ if((utf8_end_of_code_point_mask & 0xFFFF) == 0xffff) {
+ // We process in chunks of 16 bytes
+ // The routine in simd.h is reused.
+ simd8<int8_t> temp{vreinterpretq_s8_u8(in)};
+ temp.store_ascii_as_utf16<big_endian>(utf16_output);
+ utf16_output += 16; // We wrote 16 16-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+
+ // 3 byte sequences are the next most common, as seen in CJK, which has long sequences
+ // of these.
+ if (input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units.
+ uint16x4_t composed = convert_utf8_3_byte_to_utf16(in);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ composed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(composed)));
+ }
+ vst1_u16(reinterpret_cast<uint16_t*>(utf16_output), composed);
+ utf16_output += 4; // We wrote 4 16-bit characters.
+ return 12; // We consumed 12 bytes.
+ }
+
+ // 2 byte sequences occur in short bursts in languages like Greek and Russian.
+ if ((utf8_end_of_code_point_mask & 0xFFF) == 0xaaa) {
+ // We want to take 6 2-byte UTF-8 code units and turn them into 6 2-byte UTF-16 code units.
+ uint16x8_t composed = convert_utf8_2_byte_to_utf16(in);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed)));
+ }
+ vst1q_u16(reinterpret_cast<uint16_t *>(utf16_output), composed);
+
+ utf16_output += 6; // We wrote 6 16-bit characters.
+ return 12; // We consumed 12 bytes.
+ }
+
+ /// We do not have a fast path available, or the fast path is unimportant, so we fallback.
+ const uint8_t idx =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+
+ const uint8_t consumed =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // Convert to UTF-16
+ uint16x8_t composed = convert_utf8_1_to_2_byte_to_utf16(in, idx);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed)));
+ }
+ // Store
+ vst1q_u16(reinterpret_cast<uint16_t*>(utf16_output), composed);
+ utf16_output += 6; // We wrote 6 16-bit characters.
+ return consumed;
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ // UTF-16 and UTF-32 use similar algorithms, but UTF-32 skips the narrowing.
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[idx]));
+ // XXX: depending on the system scalar instructions might be faster.
+ // 1 byte: 00000000 00000000 0ccccccc
+ // 2 byte: 00000000 110bbbbb 10cccccc
+ // 3 byte: 1110aaaa 10bbbbbb 10cccccc
+ uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh));
+ // 1 byte: 00000000 0ccccccc
+ // 2 byte: xx0bbbbb x0cccccc
+ // 3 byte: xxbbbbbb x0cccccc
+ uint16x4_t lowperm = vmovn_u32(perm);
+ // Partially mask with bic (doesn't require a temporary register unlike and)
+ // The shift left insert below will clear the top bits.
+ // 1 byte: 00000000 00000000
+ // 2 byte: xx0bbbbb 00000000
+ // 3 byte: xxbbbbbb 00000000
+ uint16x4_t middlebyte = vbic_u16(lowperm, vmov_n_u16(uint16_t(~0xFF00)));
+ // ASCII
+ // 1 byte: 00000000 0ccccccc
+ // 2+byte: 00000000 00cccccc
+ uint16x4_t ascii = vand_u16(lowperm, vmov_n_u16(0x7F));
+ // Split into narrow vectors.
+ // 2 byte: 00000000 00000000
+ // 3 byte: 00000000 xxxxaaaa
+ uint16x4_t highperm = vshrn_n_u32(perm, 16);
+ // Shift right accumulate the middle byte
+ // 1 byte: 00000000 0ccccccc
+ // 2 byte: 00xx0bbb bbcccccc
+ // 3 byte: 00xxbbbb bbcccccc
+ uint16x4_t middlelow = vsra_n_u16(ascii, middlebyte, 2);
+ // Shift left and insert the top 4 bits, overwriting the garbage
+ // 1 byte: 00000000 0ccccccc
+ // 2 byte: 00000bbb bbcccccc
+ // 3 byte: aaaabbbb bbcccccc
+ uint16x4_t composed = vsli_n_u16(middlelow, highperm, 12);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ composed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(composed)));
+ }
+ vst1_u16(reinterpret_cast<uint16_t*>(utf16_output), composed);
+
+ utf16_output += 4; // We wrote 4 16-bit codepoints
+ return consumed;
+ } else if (idx < 209) {
+ // THREE (3) input code-code units
+ if (input_utf8_end_of_code_point_mask == 0x888) {
+ // We want to take 3 4-byte UTF-8 code units and turn them into 3 4-byte UTF-16 pairs.
+ // Generating surrogate pairs is a little tricky though, but it is easier when we
+ // can assume they are all pairs.
+ // This version does not use the LUT, but 4 byte sequences are less common and the
+ // overhead of the extra memory access is less important than the early branch overhead
+ // in shorter sequences.
+
+ // Swap byte pairs
+ // 10dddddd 10cccccc|10bbbbbb 11110aaa
+ // 10cccccc 10dddddd|11110aaa 10bbbbbb
+ uint8x16_t swap = vrev16q_u8(in);
+ // Shift left 2 bits
+ // cccccc00 dddddd00 xxxxxxxx bbbbbb00
+ uint32x4_t shift = vreinterpretq_u32_u8(vshlq_n_u8(swap, 2));
+ // Create a magic number containing the low 2 bits of the trail surrogate and all the
+ // corrections needed to create the pair.
+ // UTF-8 4b prefix = -0x0000|0xF000
+ // surrogate offset = -0x0000|0x0040 (0x10000 << 6)
+ // surrogate high = +0x0000|0xD800
+ // surrogate low = +0xDC00|0x0000
+ // -------------------------------
+ // = +0xDC00|0xE7C0
+ uint32x4_t magic = vmovq_n_u32(0xDC00E7C0);
+ // Generate unadjusted trail surrogate minus lowest 2 bits
+ // xxxxxxxx xxxxxxxx|11110aaa bbbbbb00
+ uint32x4_t trail = vbslq_u32(vmovq_n_u32(0x0000FF00), vreinterpretq_u32_u8(swap), shift);
+ // Insert low 2 bits of trail surrogate to magic number for later
+ // 11011100 00000000 11100111 110000cc
+ uint16x8_t magic_with_low_2 = vreinterpretq_u16_u32(vsraq_n_u32(magic, shift, 30));
+ // Generate lead surrogate
+ // xxxxcccc ccdddddd|xxxxxxxx xxxxxxxx
+ uint32x4_t lead = vreinterpretq_u32_u16(vsliq_n_u16(vreinterpretq_u16_u8(swap), vreinterpretq_u16_u8(in), 6));
+ // Mask out lead
+ // 000000cc ccdddddd|xxxxxxxx xxxxxxxx
+ lead = vbicq_u32(lead, vmovq_n_u32(uint32_t(~0x03FFFFFF)));
+ // Blend pairs
+ // 000000cc ccdddddd|11110aaa bbbbbb00
+ uint16x8_t blend = vreinterpretq_u16_u32(vbslq_u32(vmovq_n_u32(0x0000FFFF), trail, lead));
+ // Add magic number to finish the result
+ // 110111CC CCDDDDDD|110110AA BBBBBBCC
+ uint16x8_t composed = vaddq_u16(blend, magic_with_low_2);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ composed = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(composed)));
+ }
+ vst1q_u16(reinterpret_cast<uint16_t *>(utf16_output), composed);
+ utf16_output += 6; // We 3 32-bit surrogate pairs.
+ return 12; // We consumed 12 bytes.
+ }
+ // 3 1-4 byte sequences
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[idx]));
+
+ // 1 byte: 00000000 00000000 00000000 0ddddddd
+ // 3 byte: 00000000 00000000 110ccccc 10dddddd
+ // 3 byte: 00000000 1110bbbb 10cccccc 10dddddd
+ // 4 byte: 11110aaa 10bbbbbb 10cccccc 10dddddd
+ uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh));
+ // Mask the low and middle bytes
+ // 00000000 00000000 00000000 0ddddddd
+ uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7f));
+ // Because the surrogates need more work, the high surrogate is computed first.
+ uint32x4_t middlehigh = vshlq_n_u32(perm, 2);
+ // 00000000 00000000 00cccccc 00000000
+ uint32x4_t middlebyte = vandq_u32(perm, vmovq_n_u32(0x3F00));
+ // Start assembling the sequence. Since the 4th byte is in the same position as it
+ // would be in a surrogate and there is no dependency, shift left instead of right.
+ // 3 byte: 00000000 10bbbbxx xxxxxxxx xxxxxxxx
+ // 4 byte: 11110aaa bbbbbbxx xxxxxxxx xxxxxxxx
+ uint32x4_t ab = vbslq_u32(vmovq_n_u32(0xFF000000), perm, middlehigh);
+ // Top 16 bits contains the high ten bits of the surrogate pair before correction
+ // 3 byte: 00000000 10bbbbcc|cccc0000 00000000
+ // 4 byte: 11110aaa bbbbbbcc|cccc0000 00000000 - high 10 bits correct w/o correction
+ uint32x4_t abc = vbslq_u32(vmovq_n_u32(0xFFFC0000), ab, vshlq_n_u32(middlebyte, 4));
+ // Combine the low 6 or 7 bits by a shift right accumulate
+ // 3 byte: 00000000 00000010|bbbbcccc ccdddddd - low 16 bits correct
+ // 4 byte: 00000011 110aaabb|bbbbcccc ccdddddd - low 10 bits correct w/o correction
+ uint32x4_t composed = vsraq_n_u32(ascii, abc, 6);
+ // After this is for surrogates
+ // Blend the low and high surrogates
+ // 4 byte: 11110aaa bbbbbbcc|bbbbcccc ccdddddd
+ uint32x4_t mixed = vbslq_u32(vmovq_n_u32(0xFFFF0000), abc, composed);
+ // Clear the upper 6 bits of the low surrogate. Don't clear the upper bits yet as
+ // 0x10000 was not subtracted from the codepoint yet.
+ // 4 byte: 11110aaa bbbbbbcc|000000cc ccdddddd
+ uint16x8_t masked_pair =
+ vreinterpretq_u16_u32(vbicq_u32(mixed, vmovq_n_u32(uint32_t(~0xFFFF03FF))));
+ // Correct the remaining UTF-8 prefix, surrogate offset, and add the surrogate prefixes
+ // in one magic 16-bit addition.
+ // similar magic number but without the continue byte adjust and halfword swapped
+ // UTF-8 4b prefix = -0xF000|0x0000
+ // surrogate offset = -0x0040|0x0000 (0x10000 << 6)
+ // surrogate high = +0xD800|0x0000
+ // surrogate low = +0x0000|0xDC00
+ // -----------------------------------
+ // = +0xE7C0|0xDC00
+ uint16x8_t magic = vreinterpretq_u16_u32(vmovq_n_u32(0xE7C0DC00));
+ // 4 byte: 110110AA BBBBBBCC|110111CC CCDDDDDD - surrogate pair complete
+ uint32x4_t surrogates = vreinterpretq_u32_u16(vaddq_u16(masked_pair, magic));
+ // If the high bit is 1 (s32 less than zero), this needs a surrogate pair
+ uint32x4_t is_pair = vcltzq_s32(vreinterpretq_s32_u32(perm));
+
+ // Select either the 4 byte surrogate pair or the 2 byte solo codepoint
+ // 3 byte: 0xxxxxxx xxxxxxxx|bbbbcccc ccdddddd
+ // 4 byte: 110110AA BBBBBBCC|110111CC CCDDDDDD
+ uint32x4_t selected = vbslq_u32(is_pair, surrogates, composed);
+ // Byte swap if necessary
+ if (!match_system(big_endian)) {
+ selected = vreinterpretq_u32_u8(vrev16q_u8(vreinterpretq_u8_u32(selected)));
+ }
+ // Attempting to shuffle and store would be complex, just scalarize.
+ uint32_t buffer[4];
+ vst1q_u32(buffer, selected);
+ // Test for the top bit of the surrogate mask.
+ const uint32_t SURROGATE_MASK = match_system(big_endian) ? 0x80000000 : 0x00800000;
+ for (size_t i = 0; i < 3; i++) {
+ // Surrogate
+ if (buffer[i] & SURROGATE_MASK) {
+ utf16_output[0] = uint16_t(buffer[i] >> 16);
+ utf16_output[1] = uint16_t(buffer[i] & 0xFFFF);
+ utf16_output += 2;
+ } else {
+ utf16_output[0] = uint16_t(buffer[i] & 0xFFFF);
+ utf16_output++;
+ }
+ }
+ return consumed;
+ } else {
+ // here we know that there is an error but we do not handle errors
+ return 12;
+ }
+}
+
+/* end file src/arm64/arm_convert_utf8_to_utf16.cpp */
+/* begin file src/arm64/arm_convert_utf8_to_utf32.cpp */
+// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+size_t convert_masked_utf8_to_utf32(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char32_t *&utf32_out) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ uint32_t*& utf32_output = reinterpret_cast<uint32_t*&>(utf32_out);
+ uint8x16_t in = vld1q_u8(reinterpret_cast<const uint8_t*>(input));
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xFFF;
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ // We first try a few fast paths.
+ if((utf8_end_of_code_point_mask & 0xffff) == 0xffff) {
+ // We process in chunks of 16 bytes.
+ // use fast implementation in src/simdutf/arm64/simd.h
+ // Ideally the compiler can keep the tables in registers.
+ simd8<int8_t> temp{vreinterpretq_s8_u8(in)};
+ temp.store_ascii_as_utf32_tbl(utf32_out);
+ utf32_output += 16; // We wrote 16 32-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ if(input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units.
+ // Convert to UTF-16
+ uint16x4_t composed_utf16 = convert_utf8_3_byte_to_utf16(in);
+ // Zero extend and store via ST2 with a zero.
+ uint16x4x2_t interleaver = {{ composed_utf16, vmov_n_u16(0) }};
+ vst2_u16(reinterpret_cast<uint16_t *>(utf32_output), interleaver);
+ utf32_output += 4; // We wrote 4 32-bit characters.
+ return 12; // We consumed 12 bytes.
+ }
+
+ // 2 byte sequences occur in short bursts in languages like Greek and Russian.
+ if(input_utf8_end_of_code_point_mask == 0xaaa) {
+ // We want to take 6 2-byte UTF-8 code units and turn them into 6 4-byte UTF-32 code units.
+ // Convert to UTF-16
+ uint16x8_t composed_utf16 = convert_utf8_2_byte_to_utf16(in);
+ // Zero extend and store via ST2 with a zero.
+ uint16x8x2_t interleaver = {{ composed_utf16, vmovq_n_u16(0) }};
+ vst2q_u16(reinterpret_cast<uint16_t *>(utf32_output), interleaver);
+ utf32_output += 6; // We wrote 6 32-bit characters.
+ return 12; // We consumed 12 bytes.
+ }
+ /// Either no fast path or an unimportant fast path.
+
+ const uint8_t idx =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+
+
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // Convert to UTF-16
+ uint16x8_t composed_utf16 = convert_utf8_1_to_2_byte_to_utf16(in, idx);
+ // Zero extend and store with ST2 and zero
+ uint16x8x2_t interleaver = {{ composed_utf16, vmovq_n_u16(0) }};
+ vst2q_u16(reinterpret_cast<uint16_t *>(utf32_output), interleaver);
+ utf32_output += 6; // We wrote 6 32-bit characters.
+ return consumed;
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ // UTF-16 and UTF-32 use similar algorithms, but UTF-32 skips the narrowing.
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[idx]));
+ // Shuffle
+ // 1 byte: 00000000 00000000 0ccccccc
+ // 2 byte: 00000000 110bbbbb 10cccccc
+ // 3 byte: 1110aaaa 10bbbbbb 10cccccc
+ uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh));
+ // Split
+ // 00000000 00000000 0ccccccc
+ uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7F)); // 6 or 7 bits
+ // Note: unmasked
+ // xxxxxxxx aaaaxxxx xxxxxxxx
+ uint32x4_t high = vshrq_n_u32(perm, 4); // 4 bits
+ // Use 16 bit bic instead of and.
+ // The top bits will be corrected later in the bsl
+ // 00000000 10bbbbbb 00000000
+ uint32x4_t middle =
+ vreinterpretq_u32_u16(vbicq_u16(vreinterpretq_u16_u32(perm), vmovq_n_u16(uint16_t(~0xff00)))); // 5 or 6 bits
+ // Combine low and middle with shift right accumulate
+ // 00000000 00xxbbbb bbcccccc
+ uint32x4_t lowmid = vsraq_n_u32(ascii, middle, 2);
+ // Insert top 4 bits from high byte with bitwise select
+ // 00000000 aaaabbbb bbcccccc
+ uint32x4_t composed = vbslq_u32(vmovq_n_u32(0x0000F000), high, lowmid);
+ vst1q_u32(utf32_output, composed);
+ utf32_output += 4; // We wrote 4 32-bit characters.
+ return consumed;
+ } else if (idx < 209) {
+ // THREE (3) input code-code units
+ if (input_utf8_end_of_code_point_mask == 0x888) {
+ // We want to take 3 4-byte UTF-8 code units and turn them into 3 4-byte UTF-32 code units.
+ // This uses the same method as the fixed 3 byte version, reversing and shift left insert.
+ // However, there is no need for a shuffle mask now, just rev16 and rev32.
+ //
+ // This version does not use the LUT, but 4 byte sequences are less common and the
+ // overhead of the extra memory access is less important than the early branch overhead
+ // in shorter sequences, so it comes last.
+
+ // Swap pairs of bytes
+ // 10dddddd|10cccccc|10bbbbbb|11110aaa
+ // 10cccccc 10dddddd|11110aaa 10bbbbbb
+ uint16x8_t swap1 = vreinterpretq_u16_u8(vrev16q_u8(in));
+ // Shift left and insert
+ // xxxxcccc ccdddddd|xxxxxxxa aabbbbbb
+ uint16x8_t merge1 = vsliq_n_u16(swap1, vreinterpretq_u16_u8(in), 6);
+ // Swap 16-bit lanes
+ // xxxxcccc ccdddddd xxxxxxxa aabbbbbb
+ // xxxxxxxa aabbbbbb xxxxcccc ccdddddd
+ uint32x4_t swap2 = vreinterpretq_u32_u16(vrev32q_u16(merge1));
+ // Shift insert again
+ // xxxxxxxx xxxaaabb bbbbcccc ccdddddd
+ uint32x4_t merge2 = vsliq_n_u32(swap2, vreinterpretq_u32_u16(merge1), 12);
+ // Clear the garbage
+ // 00000000 000aaabb bbbbcccc ccdddddd
+ uint32x4_t composed = vandq_u32(merge2, vmovq_n_u32(0x1FFFFF));
+ // Store
+ vst1q_u32(utf32_output, composed);
+
+ utf32_output += 3; // We wrote 3 32-bit characters.
+ return 12; // We consumed 12 bytes.
+ }
+ // Unlike UTF-16, doing a fast codepath doesn't have nearly as much benefit due to
+ // surrogates no longer being involved.
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[idx]));
+ // 1 byte: 00000000 00000000 00000000 0ddddddd
+ // 2 byte: 00000000 00000000 110ccccc 10dddddd
+ // 3 byte: 00000000 1110bbbb 10cccccc 10dddddd
+ // 4 byte: 11110aaa 10bbbbbb 10cccccc 10dddddd
+ uint32x4_t perm = vreinterpretq_u32_u8(vqtbl1q_u8(in, sh));
+ // Ascii
+ uint32x4_t ascii = vandq_u32(perm, vmovq_n_u32(0x7F));
+ uint32x4_t middle = vandq_u32(perm, vmovq_n_u32(0x3f00));
+ // When converting the way we do, the 3 byte prefix will be interpreted as the
+ // 18th bit being set, since the code would interpret the lead byte (0b1110bbbb)
+ // as a continuation byte (0b10bbbbbb). To fix this, we can either xor or do an
+ // 8 bit add of the 6th bit shifted right by 1. Since NEON has shift right accumulate,
+ // we use that.
+ // 4 byte 3 byte
+ // 10bbbbbb 1110bbbb
+ // 00000000 01000000 6th bit
+ // 00000000 00100000 shift right
+ // 10bbbbbb 0000bbbb add
+ // 00bbbbbb 0000bbbb mask
+ uint8x16_t correction =
+ vreinterpretq_u8_u32(vandq_u32(perm, vmovq_n_u32(0x00400000)));
+ uint32x4_t corrected =
+ vreinterpretq_u32_u8(vsraq_n_u8(vreinterpretq_u8_u32(perm), correction, 1));
+ // 00000000 00000000 0000cccc ccdddddd
+ uint32x4_t cd = vsraq_n_u32(ascii, middle, 2);
+ // Insert twice
+ // xxxxxxxx xxxaaabb bbbbxxxx xxxxxxxx
+ uint32x4_t ab = vbslq_u32(vmovq_n_u32(0x01C0000), vshrq_n_u32(corrected, 6), vshrq_n_u32(corrected, 4));
+ // 00000000 000aaabb bbbbcccc ccdddddd
+ uint32x4_t composed = vbslq_u32(vmovq_n_u32(0xFFE00FFF), cd, ab);
+ // Store
+ vst1q_u32(utf32_output, composed);
+ utf32_output += 3; // We wrote 3 32-bit characters.
+ return consumed;
+ } else {
+ // here we know that there is an error but we do not handle errors
+ return 12;
+ }
+}
+/* end file src/arm64/arm_convert_utf8_to_utf32.cpp */
+/* begin file src/arm64/arm_convert_utf8_to_latin1.cpp */
+// Convert up to 16 bytes from utf8 to utf16 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 16, usually 12).
+size_t convert_masked_utf8_to_latin1(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char *&latin1_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ uint8x16_t in = vld1q_u8(reinterpret_cast<const uint8_t*>(input));
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+
+ // We first try a few fast paths.
+ // The obvious first test is ASCII, which actually consumes the full 16.
+ if((utf8_end_of_code_point_mask & 0xFFFF) == 0xffff) {
+ // We process in chunks of 16 bytes
+ vst1q_u8(reinterpret_cast<uint8_t*>(latin1_output), in);
+ latin1_output += 16; // We wrote 16 18-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ /// We do not have a fast path available, or the fast path is unimportant, so we fallback.
+ const uint8_t idx =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+
+ const uint8_t consumed =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ // this indicates an invalid input:
+ if(idx >= 64) { return consumed; }
+ // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere.
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes.
+ // Converts 6 1-2 byte UTF-8 characters to 6 UTF-16 characters.
+ // This is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes.
+ uint8x16_t sh = vld1q_u8(reinterpret_cast<const uint8_t*>(simdutf::tables::utf8_to_utf16::shufutf8[idx]));
+ // Shuffle
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 110aaaaa 10bbbbbb
+ uint16x8_t perm = vreinterpretq_u16_u8(vqtbl1q_u8(in, sh));
+ // Mask
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 00000000 00bbbbbb
+ uint16x8_t ascii = vandq_u16(perm, vmovq_n_u16(0x7f)); // 6 or 7 bits
+ // 1 byte: 00000000 00000000
+ // 2 byte: 000aaaaa 00000000
+ uint16x8_t highbyte = vandq_u16(perm, vmovq_n_u16(0x1f00)); // 5 bits
+ // Combine with a shift right accumulate
+ // 1 byte: 00000000 0bbbbbbb
+ // 2 byte: 00000aaa aabbbbbb
+ uint16x8_t composed = vsraq_n_u16(ascii, highbyte, 2);
+ // writing 8 bytes even though we only care about the first 6 bytes.
+ uint8x8_t latin1_packed = vmovn_u16(composed);
+ vst1_u8(reinterpret_cast<uint8_t*>(latin1_output), latin1_packed);
+ latin1_output += 6; // We wrote 6 bytes.
+ return consumed;
+}
+
+/* end file src/arm64/arm_convert_utf8_to_latin1.cpp */
+
+/* begin file src/arm64/arm_convert_utf16_to_latin1.cpp */
+
+template <endianness big_endian>
+std::pair<const char16_t*, char*> arm_convert_utf16_to_latin1(const char16_t* buf, size_t len, char* latin1_output) {
+ const char16_t* end = buf + len;
+ while (buf + 8 <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+ if (vmaxvq_u16(in) <= 0xff) {
+ // 1. pack the bytes
+ uint8x8_t latin1_packed = vmovn_u16(in);
+ // 2. store (8 bytes)
+ vst1_u8(reinterpret_cast<uint8_t*>(latin1_output), latin1_packed);
+ // 3. adjust pointers
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ return std::make_pair(nullptr, reinterpret_cast<char*>(latin1_output));
+ }
+ } // while
+ return std::make_pair(buf, latin1_output);
+}
+
+template <endianness big_endian>
+std::pair<result, char*> arm_convert_utf16_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+ while (buf + 8 <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+ if (vmaxvq_u16(in) <= 0xff) {
+ // 1. pack the bytes
+ uint8x8_t latin1_packed = vmovn_u16(in);
+ // 2. store (8 bytes)
+ vst1_u8(reinterpret_cast<uint8_t*>(latin1_output), latin1_packed);
+ // 3. adjust pointers
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ // Let us do a scalar fallback.
+ for(int k = 0; k < 8; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if(word <= 0xff) {
+ *latin1_output++ = char(word);
+ } else {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output);
+ }
+ }
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output);
+}
+/* end file src/arm64/arm_convert_utf16_to_latin1.cpp */
+/* begin file src/arm64/arm_convert_utf16_to_utf8.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ is in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char*> arm_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_out) {
+ uint8_t * utf8_output = reinterpret_cast<uint8_t*>(utf8_out);
+ const char16_t* end = buf + len;
+
+ const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800);
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+ const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+ while (buf + 16 + safety_margin <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+ if(vmaxvq_u16(in) <= 0x7F) { // ASCII fast path!!!!
+ // It is common enough that we have sequences of 16 consecutive ASCII characters.
+ uint16x8_t nextin = vld1q_u16(reinterpret_cast<const uint16_t *>(buf) + 8);
+ if (!match_system(big_endian)) { nextin = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(nextin))); }
+ if(vmaxvq_u16(nextin) > 0x7F) {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x8_t utf8_packed = vmovn_u16(in);
+ // 2. store (8 bytes)
+ vst1_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ in = nextin;
+ } else {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x16_t utf8_packed = vmovn_high_u16(vmovn_u16(in), nextin);
+ // 2. store (16 bytes)
+ vst1q_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ if (vmaxvq_u16(in) <= 0x7FF) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00);
+ const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const uint16x8_t t0 = vshlq_n_u16(in, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const uint16x8_t t1 = vandq_u16(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const uint16x8_t t2 = vandq_u16(in, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const uint16x8_t t3 = vorrq_u16(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const uint16x8_t t4 = vorrq_u16(t3, v_c080);
+ // 2. merge ASCII and 2-byte codewords
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f);
+ const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in, t4));
+ // 3. prepare bitmask for 8-bit lookup
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080);
+#else
+ const uint16x8_t mask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080 };
+#endif
+ uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask));
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const uint8x16_t shuffle = vld1q_u8(row + 1);
+ const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ vst1q_u8(utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+
+ }
+ const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800);
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (vmaxvq_u16(surrogates_bytemask) == 0) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+#else
+ const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e};
+#endif
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) vmovq_n_u16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(dup_even)));
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000));
+
+ // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa]
+ const uint16x8_t s0 = vshrq_n_u16(in, 12);
+ // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000]
+ const uint16x8_t s1 = vandq_u16(in, simdutf_vec(0b0000111111000000));
+ // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000]
+ const uint16x8_t s1s = vshlq_n_u16(s1, 2);
+ // [00bb|bbbb|0000|aaaa]
+ const uint16x8_t s2 = vorrq_u16(s0, s1s);
+ // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000));
+ const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF);
+ const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(in, v_07ff);
+ const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask);
+ const uint16x8_t s4 = veorq_u16(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4));
+ const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4));
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f);
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 );
+ const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 );
+#else
+ const uint16x8_t onemask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 };
+ const uint16x8_t twomask = { 0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 };
+#endif
+ const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask));
+ const uint16_t mask = vaddvq_u16(combined);
+ // The following fast path may or may not be beneficial.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0};
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle);
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += 12;
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const uint8x16_t shuffle0 = vld1q_u8(row0 + 1);
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const uint8x16_t shuffle1 = vld1q_u8(row1 + 1);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1);
+
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += row0[0];
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, reinterpret_cast<char*>(utf8_output)); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(buf, reinterpret_cast<char*>(utf8_output));
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char*> arm_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_out) {
+ uint8_t * utf8_output = reinterpret_cast<uint8_t*>(utf8_out);
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+
+ const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800);
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+ const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+ if(vmaxvq_u16(in) <= 0x7F) { // ASCII fast path!!!!
+ // It is common enough that we have sequences of 16 consecutive ASCII characters.
+ uint16x8_t nextin = vld1q_u16(reinterpret_cast<const uint16_t *>(buf) + 8);
+ if (!match_system(big_endian)) { nextin = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(nextin))); }
+ if(vmaxvq_u16(nextin) > 0x7F) {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x8_t utf8_packed = vmovn_u16(in);
+ // 2. store (8 bytes)
+ vst1_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ in = nextin;
+ } else {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x16_t utf8_packed = vmovn_high_u16(vmovn_u16(in), nextin);
+ // 2. store (16 bytes)
+ vst1q_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ if (vmaxvq_u16(in) <= 0x7FF) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00);
+ const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const uint16x8_t t0 = vshlq_n_u16(in, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const uint16x8_t t1 = vandq_u16(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const uint16x8_t t2 = vandq_u16(in, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const uint16x8_t t3 = vorrq_u16(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const uint16x8_t t4 = vorrq_u16(t3, v_c080);
+ // 2. merge ASCII and 2-byte codewords
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f);
+ const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, in, t4));
+ // 3. prepare bitmask for 8-bit lookup
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080);
+#else
+ const uint16x8_t mask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080 };
+#endif
+ uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask));
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const uint8x16_t shuffle = vld1q_u8(row + 1);
+ const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ vst1q_u8(utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+
+ }
+ const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800);
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (vmaxvq_u16(surrogates_bytemask) == 0) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+#else
+ const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e};
+#endif
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) vmovq_n_u16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(in), vreinterpretq_u8_u16(dup_even)));
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000));
+
+ // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa]
+ const uint16x8_t s0 = vshrq_n_u16(in, 12);
+ // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000]
+ const uint16x8_t s1 = vandq_u16(in, simdutf_vec(0b0000111111000000));
+ // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000]
+ const uint16x8_t s1s = vshlq_n_u16(s1, 2);
+ // [00bb|bbbb|0000|aaaa]
+ const uint16x8_t s2 = vorrq_u16(s0, s1s);
+ // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000));
+ const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF);
+ const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(in, v_07ff);
+ const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask);
+ const uint16x8_t s4 = veorq_u16(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4));
+ const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4));
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(in, v_007f);
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 );
+ const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 );
+#else
+ const uint16x8_t onemask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 };
+ const uint16x8_t twomask = { 0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 };
+#endif
+ const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask));
+ const uint16_t mask = vaddvq_u16(combined);
+ // The following fast path may or may not be beneficial.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0};
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle);
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += 12;
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const uint8x16_t shuffle0 = vld1q_u8(row0 + 1);
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const uint8x16_t shuffle1 = vld1q_u8(row1 + 1);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1);
+
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += row0[0];
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), reinterpret_cast<char*>(utf8_output)); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast<char*>(utf8_output));
+}
+/* end file src/arm64/arm_convert_utf16_to_utf8.cpp */
+/* begin file src/arm64/arm_convert_utf16_to_utf32.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ is in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char32_t*> arm_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_out) {
+ uint32_t * utf32_output = reinterpret_cast<uint32_t*>(utf32_out);
+ const char16_t* end = buf + len;
+
+ const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800);
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+
+ while (buf + 8 <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+
+ const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800);
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (vmaxvq_u16(surrogates_bytemask) == 0) {
+ // case: no surrogate pairs, extend all 16-bit code units to 32-bit code units
+ vst1q_u32(utf32_output, vmovl_u16(vget_low_u16(in)));
+ vst1q_u32(utf32_output+4, vmovl_high_u16(in));
+ utf32_output += 8;
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, reinterpret_cast<char32_t*>(utf32_output)); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(buf, reinterpret_cast<char32_t*>(utf32_output));
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char32_t*> arm_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_out) {
+ uint32_t * utf32_output = reinterpret_cast<uint32_t*>(utf32_out);
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+
+ const uint16x8_t v_f800 = vmovq_n_u16((uint16_t)0xf800);
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+
+ while (buf + 8 <= end) {
+ uint16x8_t in = vld1q_u16(reinterpret_cast<const uint16_t *>(buf));
+ if (!match_system(big_endian)) { in = vreinterpretq_u16_u8(vrev16q_u8(vreinterpretq_u8_u16(in))); }
+
+ const uint16x8_t surrogates_bytemask = vceqq_u16(vandq_u16(in, v_f800), v_d800);
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (vmaxvq_u16(surrogates_bytemask) == 0) {
+ // case: no surrogate pairs, extend all 16-bit code units to 32-bit code units
+ vst1q_u32(utf32_output, vmovl_u16(vget_low_u16(in)));
+ vst1q_u32(utf32_output+4, vmovl_high_u16(in));
+ utf32_output += 8;
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k + 1]) : buf[k + 1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), reinterpret_cast<char32_t*>(utf32_output)); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast<char32_t*>(utf32_output));
+}
+/* end file src/arm64/arm_convert_utf16_to_utf32.cpp */
+
+/* begin file src/arm64/arm_convert_utf32_to_latin1.cpp */
+std::pair<const char32_t*, char*> arm_convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) {
+ const char32_t* end = buf + len;
+ while (buf + 8 <= end) {
+ uint32x4_t in1 = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+ uint32x4_t in2 = vld1q_u32(reinterpret_cast<const uint32_t *>(buf+4));
+
+ uint16x8_t utf16_packed = vcombine_u16(vqmovn_u32(in1), vqmovn_u32(in2));
+ if (vmaxvq_u16(utf16_packed) <= 0xff) {
+ // 1. pack the bytes
+ uint8x8_t latin1_packed = vmovn_u16(utf16_packed);
+ // 2. store (8 bytes)
+ vst1_u8(reinterpret_cast<uint8_t*>(latin1_output), latin1_packed);
+ // 3. adjust pointers
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ return std::make_pair(nullptr, reinterpret_cast<char*>(latin1_output));
+ }
+ } // while
+ return std::make_pair(buf, latin1_output);
+}
+
+
+std::pair<result, char*> arm_convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) {
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ while (buf + 8 <= end) {
+ uint32x4_t in1 = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+ uint32x4_t in2 = vld1q_u32(reinterpret_cast<const uint32_t *>(buf+4));
+
+ uint16x8_t utf16_packed = vcombine_u16(vqmovn_u32(in1), vqmovn_u32(in2));
+
+ if (vmaxvq_u16(utf16_packed) <= 0xff) {
+ // 1. pack the bytes
+ uint8x8_t latin1_packed = vmovn_u16(utf16_packed);
+ // 2. store (8 bytes)
+ vst1_u8(reinterpret_cast<uint8_t*>(latin1_output), latin1_packed);
+ // 3. adjust pointers
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ // Let us do a scalar fallback.
+ for(int k = 0; k < 8; k++) {
+ uint32_t word = buf[k];
+ if(word <= 0xff) {
+ *latin1_output++ = char(word);
+ } else {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output);
+ }
+ }
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output);
+}
+/* end file src/arm64/arm_convert_utf32_to_latin1.cpp */
+/* begin file src/arm64/arm_convert_utf32_to_utf8.cpp */
+std::pair<const char32_t*, char*> arm_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_out) {
+ uint8_t * utf8_output = reinterpret_cast<uint8_t*>(utf8_out);
+ const char32_t* end = buf + len;
+
+ const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080);
+
+ uint16x8_t forbidden_bytemask = vmovq_n_u16(0x0);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin < end) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+ uint32x4_t nextin = vld1q_u32(reinterpret_cast<const uint32_t *>(buf+4));
+
+ // Check if no bits set above 16th
+ if(vmaxvq_u32(vorrq_u32(in, nextin)) <= 0xFFFF) {
+ // Pack UTF-32 to UTF-16 safely (without surrogate pairs)
+ // Apply UTF-16 => UTF-8 routine (arm_convert_utf16_to_utf8.cpp)
+ uint16x8_t utf16_packed = vcombine_u16(vmovn_u32(in), vmovn_u32(nextin));
+ if(vmaxvq_u16(utf16_packed) <= 0x7F) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x8_t utf8_packed = vmovn_u16(utf16_packed);
+ // 2. store (8 bytes)
+ vst1_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ continue; // we are done for this round!
+ }
+
+ if (vmaxvq_u16(utf16_packed) <= 0x7FF) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00);
+ const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const uint16x8_t t0 = vshlq_n_u16(utf16_packed, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const uint16x8_t t1 = vandq_u16(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const uint16x8_t t2 = vandq_u16(utf16_packed, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const uint16x8_t t3 = vorrq_u16(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const uint16x8_t t4 = vorrq_u16(t3, v_c080);
+ // 2. merge ASCII and 2-byte codewords
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f);
+ const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, utf16_packed, t4));
+ // 3. prepare bitmask for 8-bit lookup
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080);
+ #else
+ const uint16x8_t mask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080 };
+ #endif
+ uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask));
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const uint8x16_t shuffle = vld1q_u8(row + 1);
+ const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ vst1q_u8(utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+ } else {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+ const uint16x8_t v_dfff = vmovq_n_u16((uint16_t)0xdfff);
+ forbidden_bytemask = vorrq_u16(vandq_u16(vcleq_u16(utf16_packed, v_dfff), vcgeq_u16(utf16_packed, v_d800)), forbidden_bytemask);
+
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+ #else
+ const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e};
+ #endif
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+ #define simdutf_vec(x) vmovq_n_u16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(utf16_packed), vreinterpretq_u8_u16(dup_even)));
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000));
+
+ // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa]
+ const uint16x8_t s0 = vshrq_n_u16(utf16_packed, 12);
+ // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000]
+ const uint16x8_t s1 = vandq_u16(utf16_packed, simdutf_vec(0b0000111111000000));
+ // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000]
+ const uint16x8_t s1s = vshlq_n_u16(s1, 2);
+ // [00bb|bbbb|0000|aaaa]
+ const uint16x8_t s2 = vorrq_u16(s0, s1s);
+ // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000));
+ const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF);
+ const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(utf16_packed, v_07ff);
+ const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask);
+ const uint16x8_t s4 = veorq_u16(s3, m0);
+ #undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4));
+ const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4));
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f);
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 );
+ const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 );
+ #else
+ const uint16x8_t onemask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 };
+ const uint16x8_t twomask = { 0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 };
+ #endif
+ const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask));
+ const uint16_t mask = vaddvq_u16(combined);
+ // The following fast path may or may not be beneficial.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0};
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle);
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += 12;
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const uint8x16_t shuffle0 = vld1q_u8(row0 + 1);
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const uint8x16_t shuffle1 = vld1q_u8(row1 + 1);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1);
+
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += row0[0];
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ }
+ // At least one 32-bit word will produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes.
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000)==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, reinterpret_cast<char*>(utf8_output)); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, reinterpret_cast<char*>(utf8_output)); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ // check for invalid input
+ if (vmaxvq_u16(forbidden_bytemask) != 0) {
+ return std::make_pair(nullptr, reinterpret_cast<char*>(utf8_output));
+ }
+ return std::make_pair(buf, reinterpret_cast<char*>(utf8_output));
+}
+
+
+std::pair<result, char*> arm_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_out) {
+ uint8_t * utf8_output = reinterpret_cast<uint8_t*>(utf8_out);
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ const uint16x8_t v_c080 = vmovq_n_u16((uint16_t)0xc080);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin < end) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+ uint32x4_t nextin = vld1q_u32(reinterpret_cast<const uint32_t *>(buf+4));
+
+ // Check if no bits set above 16th
+ if(vmaxvq_u32(vorrq_u32(in, nextin)) <= 0xFFFF) {
+ // Pack UTF-32 to UTF-16 safely (without surrogate pairs)
+ // Apply UTF-16 => UTF-8 routine (arm_convert_utf16_to_utf8.cpp)
+ uint16x8_t utf16_packed = vcombine_u16(vmovn_u32(in), vmovn_u32(nextin));
+ if(vmaxvq_u16(utf16_packed) <= 0x7F) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ // obviously suboptimal.
+ uint8x8_t utf8_packed = vmovn_u16(utf16_packed);
+ // 2. store (8 bytes)
+ vst1_u8(utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ continue; // we are done for this round!
+ }
+
+ if (vmaxvq_u16(utf16_packed) <= 0x7FF) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const uint16x8_t v_1f00 = vmovq_n_u16((int16_t)0x1f00);
+ const uint16x8_t v_003f = vmovq_n_u16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const uint16x8_t t0 = vshlq_n_u16(utf16_packed, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const uint16x8_t t1 = vandq_u16(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const uint16x8_t t2 = vandq_u16(utf16_packed, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const uint16x8_t t3 = vorrq_u16(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const uint16x8_t t4 = vorrq_u16(t3, v_c080);
+ // 2. merge ASCII and 2-byte codewords
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f);
+ const uint8x16_t utf8_unpacked = vreinterpretq_u8_u16(vbslq_u16(one_byte_bytemask, utf16_packed, t4));
+ // 3. prepare bitmask for 8-bit lookup
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t mask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080);
+ #else
+ const uint16x8_t mask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0002, 0x0008,
+ 0x0020, 0x0080 };
+ #endif
+ uint16_t m2 = vaddvq_u16(vandq_u16(one_byte_bytemask, mask));
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const uint8x16_t shuffle = vld1q_u8(row + 1);
+ const uint8x16_t utf8_packed = vqtbl1q_u8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ vst1q_u8(utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+ } else {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+
+ // check for invalid input
+ const uint16x8_t v_d800 = vmovq_n_u16((uint16_t)0xd800);
+ const uint16x8_t v_dfff = vmovq_n_u16((uint16_t)0xdfff);
+ const uint16x8_t forbidden_bytemask = vandq_u16(vcleq_u16(utf16_packed, v_dfff), vcgeq_u16(utf16_packed, v_d800));
+ if (vmaxvq_u16(forbidden_bytemask) != 0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), reinterpret_cast<char*>(utf8_output));
+ }
+
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t dup_even = simdutf_make_uint16x8_t(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+ #else
+ const uint16x8_t dup_even = {0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e};
+ #endif
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+ #define simdutf_vec(x) vmovq_n_u16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const uint16x8_t t0 = vreinterpretq_u16_u8(vqtbl1q_u8(vreinterpretq_u8_u16(utf16_packed), vreinterpretq_u8_u16(dup_even)));
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const uint16x8_t t1 = vandq_u16(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const uint16x8_t t2 = vorrq_u16 (t1, simdutf_vec(0b1000000000000000));
+
+ // s0: [aaaa|bbbb|bbcc|cccc] => [0000|0000|0000|aaaa]
+ const uint16x8_t s0 = vshrq_n_u16(utf16_packed, 12);
+ // s1: [aaaa|bbbb|bbcc|cccc] => [0000|bbbb|bb00|0000]
+ const uint16x8_t s1 = vandq_u16(utf16_packed, simdutf_vec(0b0000111111000000));
+ // [0000|bbbb|bb00|0000] => [00bb|bbbb|0000|0000]
+ const uint16x8_t s1s = vshlq_n_u16(s1, 2);
+ // [00bb|bbbb|0000|aaaa]
+ const uint16x8_t s2 = vorrq_u16(s0, s1s);
+ // s3: [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const uint16x8_t s3 = vorrq_u16(s2, simdutf_vec(0b1100000011100000));
+ const uint16x8_t v_07ff = vmovq_n_u16((uint16_t)0x07FF);
+ const uint16x8_t one_or_two_bytes_bytemask = vcleq_u16(utf16_packed, v_07ff);
+ const uint16x8_t m0 = vbicq_u16(simdutf_vec(0b0100000000000000), one_or_two_bytes_bytemask);
+ const uint16x8_t s4 = veorq_u16(s3, m0);
+ #undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const uint8x16_t out0 = vreinterpretq_u8_u16(vzip1q_u16(t2, s4));
+ const uint8x16_t out1 = vreinterpretq_u8_u16(vzip2q_u16(t2, s4));
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16x8_t v_007f = vmovq_n_u16((uint16_t)0x007F);
+ const uint16x8_t one_byte_bytemask = vcleq_u16(utf16_packed, v_007f);
+ #ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint16x8_t onemask = simdutf_make_uint16x8_t(0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 );
+ const uint16x8_t twomask = simdutf_make_uint16x8_t(0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 );
+ #else
+ const uint16x8_t onemask = { 0x0001, 0x0004,
+ 0x0010, 0x0040,
+ 0x0100, 0x0400,
+ 0x1000, 0x4000 };
+ const uint16x8_t twomask = { 0x0002, 0x0008,
+ 0x0020, 0x0080,
+ 0x0200, 0x0800,
+ 0x2000, 0x8000 };
+ #endif
+ const uint16x8_t combined = vorrq_u16(vandq_u16(one_byte_bytemask, onemask), vandq_u16(one_or_two_bytes_bytemask, twomask));
+ const uint16_t mask = vaddvq_u16(combined);
+ // The following fast path may or may not be beneficial.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const uint8x16_t shuffle = {2,3,1,6,7,5,10,11,9,14,15,13,0,0,0,0};
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle);
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += 12;
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const uint8x16_t shuffle0 = vld1q_u8(row0 + 1);
+ const uint8x16_t utf8_0 = vqtbl1q_u8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const uint8x16_t shuffle1 = vld1q_u8(row1 + 1);
+ const uint8x16_t utf8_1 = vqtbl1q_u8(out1, shuffle1);
+
+ vst1q_u8(utf8_output, utf8_0);
+ utf8_output += row0[0];
+ vst1q_u8(utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ }
+ // At least one 32-bit word will produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes.
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000)==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), reinterpret_cast<char*>(utf8_output)); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), reinterpret_cast<char*>(utf8_output)); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast<char*>(utf8_output));
+}
+/* end file src/arm64/arm_convert_utf32_to_utf8.cpp */
+/* begin file src/arm64/arm_convert_utf32_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char32_t*, char16_t*> arm_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_out) {
+ uint16_t * utf16_output = reinterpret_cast<uint16_t*>(utf16_out);
+ const char32_t* end = buf + len;
+
+ uint16x4_t forbidden_bytemask = vmov_n_u16(0x0);
+
+ while(buf + 4 <= end) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+
+ // Check if no bits set above 16th
+ if(vmaxvq_u32(in) <= 0xFFFF) {
+ uint16x4_t utf16_packed = vmovn_u32(in);
+
+ const uint16x4_t v_d800 = vmov_n_u16((uint16_t)0xd800);
+ const uint16x4_t v_dfff = vmov_n_u16((uint16_t)0xdfff);
+ forbidden_bytemask = vorr_u16(vand_u16(vcle_u16(utf16_packed, v_dfff), vcge_u16(utf16_packed, v_d800)), forbidden_bytemask);
+
+ if (!match_system(big_endian)) { utf16_packed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(utf16_packed))); }
+ vst1_u16(utf16_output, utf16_packed);
+ utf16_output += 4;
+ buf += 4;
+ } else {
+ size_t forward = 3;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, reinterpret_cast<char16_t*>(utf16_output)); }
+ *utf16_output++ = !match_system(big_endian) ? char16_t(word >> 8 | word << 8) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, reinterpret_cast<char16_t*>(utf16_output)); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = uint16_t(high_surrogate >> 8 | high_surrogate << 8);
+ low_surrogate = uint16_t(low_surrogate << 8 | low_surrogate >> 8);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ // check for invalid input
+ if (vmaxv_u16(forbidden_bytemask) != 0) {
+ return std::make_pair(nullptr, reinterpret_cast<char16_t*>(utf16_output));
+ }
+
+ return std::make_pair(buf, reinterpret_cast<char16_t*>(utf16_output));
+}
+
+
+template <endianness big_endian>
+std::pair<result, char16_t*> arm_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_out) {
+ uint16_t * utf16_output = reinterpret_cast<uint16_t*>(utf16_out);
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ while(buf + 4 <= end) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(buf));
+
+ // Check if no bits set above 16th
+ if(vmaxvq_u32(in) <= 0xFFFF) {
+ uint16x4_t utf16_packed = vmovn_u32(in);
+
+ const uint16x4_t v_d800 = vmov_n_u16((uint16_t)0xd800);
+ const uint16x4_t v_dfff = vmov_n_u16((uint16_t)0xdfff);
+ const uint16x4_t forbidden_bytemask = vand_u16(vcle_u16(utf16_packed, v_dfff), vcge_u16(utf16_packed, v_d800));
+ if (vmaxv_u16(forbidden_bytemask) != 0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), reinterpret_cast<char16_t*>(utf16_output));
+ }
+
+ if (!match_system(big_endian)) { utf16_packed = vreinterpret_u16_u8(vrev16_u8(vreinterpret_u8_u16(utf16_packed))); }
+ vst1_u16(utf16_output, utf16_packed);
+ utf16_output += 4;
+ buf += 4;
+ } else {
+ size_t forward = 3;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), reinterpret_cast<char16_t*>(utf16_output)); }
+ *utf16_output++ = !match_system(big_endian) ? char16_t(word >> 8 | word << 8) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), reinterpret_cast<char16_t*>(utf16_output)); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (!match_system(big_endian)) {
+ high_surrogate = uint16_t(high_surrogate >> 8 | high_surrogate << 8);
+ low_surrogate = uint16_t(low_surrogate << 8 | low_surrogate >> 8);
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), reinterpret_cast<char16_t*>(utf16_output));
+}
+/* end file src/arm64/arm_convert_utf32_to_utf16.cpp */
+/* begin file src/arm64/arm_base64.cpp */
+/**
+ * References and further reading:
+ *
+ * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the
+ * speed of a memory copy, Software: Practice and Experience 50 (2), 2020.
+ * https://arxiv.org/abs/1910.05109
+ *
+ * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2
+ * Instructions, ACM Transactions on the Web 12 (3), 2018.
+ * https://arxiv.org/abs/1704.00605
+ *
+ * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings.
+ * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force,
+ * Request for Comments: 4648.
+ *
+ * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization.
+ * http://www.alfredklomp.com/programming/sse-base64/. (2014).
+ *
+ * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD
+ * acceleration. https://github.com/aklomp/base64. (2014).
+ *
+ * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014).
+ * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/
+ *
+ * Nick Kopp. 2013. Base64 Encoding on a GPU.
+ * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013).
+ */
+
+size_t encode_base64(char *dst, const char *src, size_t srclen,
+ base64_options options) {
+ // credit: Wojciech Muła
+ uint8_t *out = (uint8_t *)dst;
+ constexpr static uint8_t source_table[64] = {
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x', 'C', 'S', 'i', 'y', 'D',
+ 'T', 'j', 'z', 'E', 'U', 'k', '0', 'F', 'V', 'l', '1', 'G', 'W',
+ 'm', '2', 'H', 'X', 'n', '3', 'I', 'Y', 'o', '4', 'J', 'Z', 'p',
+ '5', 'K', 'a', 'q', '6', 'L', 'b', 'r', '7', 'M', 'c', 's', '8',
+ 'N', 'd', 't', '9', 'O', 'e', 'u', '+', 'P', 'f', 'v', '/',
+ };
+ constexpr static uint8_t source_table_url[64] = {
+ 'A', 'Q', 'g', 'w', 'B', 'R', 'h', 'x', 'C', 'S', 'i', 'y', 'D',
+ 'T', 'j', 'z', 'E', 'U', 'k', '0', 'F', 'V', 'l', '1', 'G', 'W',
+ 'm', '2', 'H', 'X', 'n', '3', 'I', 'Y', 'o', '4', 'J', 'Z', 'p',
+ '5', 'K', 'a', 'q', '6', 'L', 'b', 'r', '7', 'M', 'c', 's', '8',
+ 'N', 'd', 't', '9', 'O', 'e', 'u', '-', 'P', 'f', 'v', '_',
+ };
+ const uint8x16_t v3f = vdupq_n_u8(0x3f);
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ // When trying to load a uint8_t array, Visual Studio might
+ // error with: error C2664: '__n128x4 neon_ld4m_q8(const char *)':
+ // cannot convert argument 1 from 'const uint8_t [64]' to 'const char *
+ const uint8x16x4_t table =
+ vld4q_u8((reinterpret_cast<const char *>(
+ options & base64_url) ? source_table_url : source_table));
+#else
+ const uint8x16x4_t table =
+ vld4q_u8((options & base64_url) ? source_table_url : source_table);
+#endif
+ size_t i = 0;
+ for (; i + 16 * 3 <= srclen; i += 16 * 3) {
+ const uint8x16x3_t in = vld3q_u8((const uint8_t *)src + i);
+ uint8x16x4_t result;
+ result.val[0] = vshrq_n_u8(in.val[0], 2);
+ result.val[1] =
+ vandq_u8(vsliq_n_u8(vshrq_n_u8(in.val[1], 4), in.val[0], 4), v3f);
+ result.val[2] =
+ vandq_u8(vsliq_n_u8(vshrq_n_u8(in.val[2], 6), in.val[1], 2), v3f);
+ result.val[3] = vandq_u8(in.val[2], v3f);
+ result.val[0] = vqtbl4q_u8(table, result.val[0]);
+ result.val[1] = vqtbl4q_u8(table, result.val[1]);
+ result.val[2] = vqtbl4q_u8(table, result.val[2]);
+ result.val[3] = vqtbl4q_u8(table, result.val[3]);
+ vst4q_u8(out, result);
+ out += 64;
+ }
+ out += scalar::base64::tail_encode_base64((char *)out, src + i, srclen - i,
+ options);
+
+ return size_t((char *)out - dst);
+}
+
+static inline void compress(uint8x16_t data, uint16_t mask, char *output) {
+ if (mask == 0) {
+ vst1q_u8((uint8_t *)output, data);
+ return;
+ }
+ uint8_t mask1 = uint8_t(mask); // least significant 8 bits
+ uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
+ uint64x2_t compactmasku64 = {tables::base64::thintable_epi8[mask1],
+ tables::base64::thintable_epi8[mask2]};
+ uint8x16_t compactmask = vreinterpretq_u8_u64(compactmasku64);
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t off =
+ simdutf_make_uint8x16_t(0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8);
+#else
+ const uint8x16_t off = {0, 0, 0, 0, 0, 0, 0, 0, 8, 8, 8, 8, 8, 8, 8, 8};
+#endif
+
+ compactmask = vaddq_u8(compactmask, off);
+ uint8x16_t pruned = vqtbl1q_u8(data, compactmask);
+
+ int pop1 = tables::base64::BitsSetTable256mul2[mask1];
+ // then load the corresponding mask, what it does is to write
+ // only the first pop1 bytes from the first 8 bytes, and then
+ // it fills in with the bytes from the second 8 bytes + some filling
+ // at the end.
+ compactmask = vld1q_u8(tables::base64::pshufb_combine_table + pop1 * 8);
+ uint8x16_t answer = vqtbl1q_u8(pruned, compactmask);
+ vst1q_u8((uint8_t *)output, answer);
+}
+
+struct block64 {
+ uint8x16_t chunks[4];
+};
+static_assert(sizeof(block64) == 64, "block64 is not 64 bytes");
+template <bool base64_url> uint64_t to_base64_mask(block64 *b, bool *error) {
+ uint8x16_t v0f = vdupq_n_u8(0xf);
+
+ uint8x16_t underscore0, underscore1, underscore2, underscore3;
+ if (base64_url) {
+ underscore0 = vceqq_u8(b->chunks[0], vdupq_n_u8(0x5f));
+ underscore1 = vceqq_u8(b->chunks[1], vdupq_n_u8(0x5f));
+ underscore2 = vceqq_u8(b->chunks[2], vdupq_n_u8(0x5f));
+ underscore3 = vceqq_u8(b->chunks[3], vdupq_n_u8(0x5f));
+ } else {
+ (void)underscore0;
+ (void)underscore1;
+ (void)underscore2;
+ (void)underscore3;
+ }
+
+ uint8x16_t lo_nibbles0 = vandq_u8(b->chunks[0], v0f);
+ uint8x16_t lo_nibbles1 = vandq_u8(b->chunks[1], v0f);
+ uint8x16_t lo_nibbles2 = vandq_u8(b->chunks[2], v0f);
+ uint8x16_t lo_nibbles3 = vandq_u8(b->chunks[3], v0f);
+ // Needed by the decoding step.
+ uint8x16_t hi_nibbles0 = vshrq_n_u8(b->chunks[0], 4);
+ uint8x16_t hi_nibbles1 = vshrq_n_u8(b->chunks[1], 4);
+ uint8x16_t hi_nibbles2 = vshrq_n_u8(b->chunks[2], 4);
+ uint8x16_t hi_nibbles3 = vshrq_n_u8(b->chunks[3], 4);
+ uint8x16_t lut_lo;
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ if (base64_url) {
+ lut_lo =
+ simdutf_make_uint8x16_t(0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x61, 0xe1, 0xf4, 0xf5, 0xa5, 0xf4, 0xf4);
+ } else {
+ lut_lo =
+ simdutf_make_uint8x16_t(0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x61, 0xe1, 0xb4, 0xf5, 0xe5, 0xf4, 0xb4);
+ }
+#else
+ if (base64_url) {
+ lut_lo = uint8x16_t{0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x61, 0xe1, 0xf4, 0xf5, 0xa5, 0xf4, 0xf4};
+ } else {
+ lut_lo = uint8x16_t{0x3a, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70, 0x70,
+ 0x70, 0x61, 0xe1, 0xb4, 0xf5, 0xe5, 0xf4, 0xb4};
+ }
+#endif
+ uint8x16_t lo0 = vqtbl1q_u8(lut_lo, lo_nibbles0);
+ uint8x16_t lo1 = vqtbl1q_u8(lut_lo, lo_nibbles1);
+ uint8x16_t lo2 = vqtbl1q_u8(lut_lo, lo_nibbles2);
+ uint8x16_t lo3 = vqtbl1q_u8(lut_lo, lo_nibbles3);
+ uint8x16_t lut_hi;
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ if (base64_url) {
+ lut_hi =
+ simdutf_make_uint8x16_t(0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20);
+ } else {
+ lut_hi =
+ simdutf_make_uint8x16_t(0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20);
+ }
+#else
+ if (base64_url) {
+ lut_hi = uint8x16_t{0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
+ } else {
+ lut_hi = uint8x16_t{0x11, 0x20, 0x42, 0x80, 0x8, 0x4, 0x8, 0x4,
+ 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20};
+ }
+#endif
+ uint8x16_t hi0 = vqtbl1q_u8(lut_hi, hi_nibbles0);
+ uint8x16_t hi1 = vqtbl1q_u8(lut_hi, hi_nibbles1);
+ uint8x16_t hi2 = vqtbl1q_u8(lut_hi, hi_nibbles2);
+ uint8x16_t hi3 = vqtbl1q_u8(lut_hi, hi_nibbles3);
+
+ if (base64_url) {
+ hi0 = vbicq_u8(hi0, underscore0);
+ hi1 = vbicq_u8(hi1, underscore1);
+ hi2 = vbicq_u8(hi2, underscore2);
+ hi3 = vbicq_u8(hi3, underscore3);
+ }
+
+ uint8_t checks =
+ vmaxvq_u8(vorrq_u8(vorrq_u8(vandq_u8(lo0, hi0), vandq_u8(lo1, hi1)),
+ vorrq_u8(vandq_u8(lo2, hi2), vandq_u8(lo3, hi3))));
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ const uint8x16_t bit_mask =
+ simdutf_make_uint8x16_t(0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80);
+#else
+ const uint8x16_t bit_mask = {0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+ 0x01, 0x02, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80};
+#endif
+ uint64_t badcharmask = 0;
+ *error = checks > 0x3;
+ if (checks) {
+ // Add each of the elements next to each other, successively, to stuff each
+ // 8 byte mask into one.
+ uint8x16_t test0 = vtstq_u8(lo0, hi0);
+ uint8x16_t test1 = vtstq_u8(lo1, hi1);
+ uint8x16_t test2 = vtstq_u8(lo2, hi2);
+ uint8x16_t test3 = vtstq_u8(lo3, hi3);
+ uint8x16_t sum0 =
+ vpaddq_u8(vandq_u8(test0, bit_mask), vandq_u8(test1, bit_mask));
+ uint8x16_t sum1 =
+ vpaddq_u8(vandq_u8(test2, bit_mask), vandq_u8(test3, bit_mask));
+ sum0 = vpaddq_u8(sum0, sum1);
+ sum0 = vpaddq_u8(sum0, sum0);
+ badcharmask = vgetq_lane_u64(vreinterpretq_u64_u8(sum0), 0);
+ }
+ // This is the transformation step that can be done while we are waiting for
+ // sum0
+ uint8x16_t roll_lut;
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+ if (base64_url) {
+ roll_lut =
+ simdutf_make_uint8x16_t(0xe0, 0x11, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
+ } else {
+ roll_lut =
+ simdutf_make_uint8x16_t(0x0, 0x10, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0);
+ }
+#else
+ if (base64_url) {
+ roll_lut = uint8x16_t{0xe0, 0x11, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+ } else {
+ roll_lut = uint8x16_t{0x0, 0x10, 0x13, 0x4, 0xbf, 0xbf, 0xb9, 0xb9,
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0};
+ }
+#endif
+ uint8x16_t vsecond_last = base64_url ? vdupq_n_u8(0x2d) : vdupq_n_u8(0x2f);
+ if (base64_url) {
+ hi_nibbles0 = vbicq_u8(hi_nibbles0, underscore0);
+ hi_nibbles1 = vbicq_u8(hi_nibbles1, underscore1);
+ hi_nibbles2 = vbicq_u8(hi_nibbles2, underscore2);
+ hi_nibbles3 = vbicq_u8(hi_nibbles3, underscore3);
+ }
+ uint8x16_t roll0 = vqtbl1q_u8(
+ roll_lut, vaddq_u8(vceqq_u8(b->chunks[0], vsecond_last), hi_nibbles0));
+ uint8x16_t roll1 = vqtbl1q_u8(
+ roll_lut, vaddq_u8(vceqq_u8(b->chunks[1], vsecond_last), hi_nibbles1));
+ uint8x16_t roll2 = vqtbl1q_u8(
+ roll_lut, vaddq_u8(vceqq_u8(b->chunks[2], vsecond_last), hi_nibbles2));
+ uint8x16_t roll3 = vqtbl1q_u8(
+ roll_lut, vaddq_u8(vceqq_u8(b->chunks[3], vsecond_last), hi_nibbles3));
+ b->chunks[0] = vaddq_u8(b->chunks[0], roll0);
+ b->chunks[1] = vaddq_u8(b->chunks[1], roll1);
+ b->chunks[2] = vaddq_u8(b->chunks[2], roll2);
+ b->chunks[3] = vaddq_u8(b->chunks[3], roll3);
+ return badcharmask;
+}
+
+void copy_block(block64 *b, char *output) {
+ vst1q_u8((uint8_t *)output, b->chunks[0]);
+ vst1q_u8((uint8_t *)output + 16, b->chunks[1]);
+ vst1q_u8((uint8_t *)output + 32, b->chunks[2]);
+ vst1q_u8((uint8_t *)output + 48, b->chunks[3]);
+}
+
+uint64_t compress_block(block64 *b, uint64_t mask, char *output) {
+ uint64_t popcounts =
+ vget_lane_u64(vreinterpret_u64_u8(vcnt_u8(vcreate_u8(~mask))), 0);
+ uint64_t offsets = popcounts * 0x0101010101010101;
+ compress(b->chunks[0], uint16_t(mask), output);
+ compress(b->chunks[1], uint16_t(mask >> 16), &output[(offsets >> 8) & 0xFF]);
+ compress(b->chunks[2], uint16_t(mask >> 32), &output[(offsets >> 24) & 0xFF]);
+ compress(b->chunks[3], uint16_t(mask >> 48), &output[(offsets >> 40) & 0xFF]);
+ return offsets >> 56;
+}
+
+// The caller of this function is responsible to ensure that there are 64 bytes available
+// from reading at src. The data is read into a block64 structure.
+void load_block(block64 *b, const char *src) {
+ b->chunks[0] = vld1q_u8(reinterpret_cast<const uint8_t *>(src));
+ b->chunks[1] = vld1q_u8(reinterpret_cast<const uint8_t *>(src) + 16);
+ b->chunks[2] = vld1q_u8(reinterpret_cast<const uint8_t *>(src) + 32);
+ b->chunks[3] = vld1q_u8(reinterpret_cast<const uint8_t *>(src) + 48);
+}
+
+// The caller of this function is responsible to ensure that there are 32 bytes available
+// from reading at data. It returns a 16-byte value, narrowing with saturation the 16-bit words.
+inline uint8x16_t load_satured(const uint16_t *data) {
+ uint16x8_t in1 = vld1q_u16(data);
+ uint16x8_t in2 = vld1q_u16(data + 8);
+ return vqmovn_high_u16(vqmovn_u16(in1), in2);
+}
+
+// The caller of this function is responsible to ensure that there are 128 bytes available
+// from reading at src. The data is read into a block64 structure.
+void load_block(block64 *b, const char16_t *src) {
+ b->chunks[0] = load_satured(reinterpret_cast<const uint16_t *>(src));
+ b->chunks[1] = load_satured(reinterpret_cast<const uint16_t *>(src) + 16);
+ b->chunks[2] = load_satured(reinterpret_cast<const uint16_t *>(src) + 32);
+ b->chunks[3] = load_satured(reinterpret_cast<const uint16_t *>(src) + 48);
+}
+
+// decode 64 bytes and output 48 bytes
+void base64_decode_block(char *out, const char *src) {
+ uint8x16x4_t str = vld4q_u8((uint8_t *)src);
+ uint8x16x3_t outvec;
+ outvec.val[0] =
+ vorrq_u8(vshlq_n_u8(str.val[0], 2), vshrq_n_u8(str.val[1], 4));
+ outvec.val[1] =
+ vorrq_u8(vshlq_n_u8(str.val[1], 4), vshrq_n_u8(str.val[2], 2));
+ outvec.val[2] = vorrq_u8(vshlq_n_u8(str.val[2], 6), str.val[3]);
+ vst3q_u8((uint8_t *)out, outvec);
+}
+
+template <bool base64_url, typename char_type>
+result compress_decode_base64(char *dst, const char_type *src, size_t srclen,
+ base64_options options) {
+ const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value
+ : tables::base64::to_base64_value;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ size_t equalsigns = 0;
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 1;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 2;
+ }
+ }
+ const char_type *const srcinit = src;
+ const char *const dstinit = dst;
+ const char_type *const srcend = src + srclen;
+
+ constexpr size_t block_size = 10;
+ char buffer[block_size * 64];
+ char *bufferptr = buffer;
+ if (srclen >= 64) {
+ const char_type *const srcend64 = src + srclen - 64;
+ while (src <= srcend64) {
+ block64 b;
+ load_block(&b, src);
+ src += 64;
+ bool error = false;
+ uint64_t badcharmask = to_base64_mask<base64_url>(&b, &error);
+ if(badcharmask)
+ if (error) {
+ src -= 64;
+
+ while (src < srcend && to_base64[uint8_t(*src)] <= 64) {
+ src++;
+ }
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+
+ if (badcharmask != 0) {
+ // optimization opportunity: check for simple masks like those made of
+ // continuous 1s followed by continuous 0s. And masks containing a
+ // single bad character.
+
+ bufferptr += compress_block(&b, badcharmask, bufferptr);
+ } else {
+ // optimization opportunity: if bufferptr == buffer and mask == 0, we
+ // can avoid the call to compress_block and decode directly.
+ copy_block(&b, bufferptr);
+ bufferptr += 64;
+ // base64_decode_block(dst, &b);
+ // dst += 48;
+ }
+ if (bufferptr >= (block_size - 1) * 64 + buffer) {
+ for (size_t i = 0; i < (block_size - 1); i++) {
+ base64_decode_block(dst, buffer + i * 64);
+ dst += 48;
+ }
+ std::memcpy(buffer, buffer + (block_size - 1) * 64,
+ 64); // 64 might be too much
+ bufferptr -= (block_size - 1) * 64;
+ }
+ }
+ }
+ char *buffer_start = buffer;
+ // Optimization note: if this is almost full, then it is worth our
+ // time, otherwise, we should just decode directly.
+ int last_block = (int)((bufferptr - buffer_start) % 64);
+ if (last_block != 0 && srcend - src + last_block >= 64) {
+ while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ *bufferptr = char(val);
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ bufferptr += (val <= 63);
+ src++;
+ }
+ }
+
+ for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
+ base64_decode_block(dst, buffer_start);
+ dst += 48;
+ }
+ if ((bufferptr - buffer_start) % 64 != 0) {
+ while (buffer_start + 4 < bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 4);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ if (buffer_start + 4 <= bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ // we may have 1, 2 or 3 bytes left and we need to decode them so let us
+ // bring in src content
+ int leftover = int(bufferptr - buffer_start);
+ if (leftover > 0) {
+ while (leftover < 4 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ buffer_start[leftover] = char(val);
+ leftover += (val <= 63);
+ src++;
+ }
+
+ if (leftover == 1) {
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ if (leftover == 2) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ dst += 1;
+ } else if (leftover == 3) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6) +
+ (uint32_t(buffer_start[2]) << 1 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+
+ std::memcpy(dst, &triple, 2);
+ dst += 2;
+ } else {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+ dst += 3;
+ }
+ }
+ }
+ if (src < srcend + equalsigns) {
+ result r =
+ scalar::base64::base64_tail_decode(dst, src, srcend - src, options);
+ if (r.error == error_code::INVALID_BASE64_CHARACTER) {
+ r.count += size_t(src - srcinit);
+ return r;
+ } else {
+ r.count += size_t(dst - dstinit);
+ }
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ r.error = error_code::INVALID_BASE64_CHARACTER;
+ }
+ }
+ return r;
+ }
+ if(equalsigns > 0) {
+ if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)};
+ }
+ }
+ return {SUCCESS, size_t(dst - dstinit)};
+}
+/* end file src/arm64/arm_base64.cpp */
+
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* begin file src/generic/buf_block_reader.h */
+namespace simdutf {
+namespace arm64 {
+namespace {
+
+// Walks through a buffer in block-sized increments, loading the last part with spaces
+template<size_t STEP_SIZE>
+struct buf_block_reader {
+public:
+ simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len);
+ simdutf_really_inline size_t block_index();
+ simdutf_really_inline bool has_full_block() const;
+ simdutf_really_inline const uint8_t *full_block() const;
+ /**
+ * Get the last block, padded with spaces.
+ *
+ * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this
+ * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there
+ * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding.
+ *
+ * @return the number of effective characters in the last block.
+ */
+ simdutf_really_inline size_t get_remainder(uint8_t *dst) const;
+ simdutf_really_inline void advance();
+private:
+ const uint8_t *buf;
+ const size_t len;
+ const size_t lenminusstep;
+ size_t idx;
+};
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text_64(const uint8_t *text) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ in.store(reinterpret_cast<uint8_t*>(buf));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ if (buf[i] < ' ') { buf[i] = '_'; }
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+simdutf_unused static char * format_mask(uint64_t mask) {
+ static char *buf = reinterpret_cast<char*>(malloc(64 + 1));
+ for (size_t i=0; i<64; i++) {
+ buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
+ }
+ buf[64] = '\0';
+ return buf;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() { return idx; }
+
+template<size_t STEP_SIZE>
+simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
+ return idx < lenminusstep;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline const uint8_t *buf_block_reader<STEP_SIZE>::full_block() const {
+ return &buf[idx];
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
+ if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers
+ std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once.
+ std::memcpy(dst, buf + idx, len - idx);
+ return len - idx;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
+ idx += STEP_SIZE;
+}
+
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/buf_block_reader.h */
+/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_validation {
+
+using namespace simd;
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+ //
+ // Return nonzero if there are incomplete multibyte characters at the end of the block:
+ // e.g. if there is a 4-byte character, but it's 3 bytes from the end.
+ //
+ simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
+ // If the previous input's last 3 bytes match this, they're too short (they ended at EOF):
+ // ... 1111____ 111_____ 11______
+ static const uint8_t max_array[32] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1
+ };
+ const simd8<uint8_t> max_value(&max_array[sizeof(max_array)-sizeof(simd8<uint8_t>)]);
+ return input.gt_bits(max_value);
+ }
+
+ struct utf8_checker {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+ // The last input we received
+ simd8<uint8_t> prev_input_block;
+ // Whether the last input we received was incomplete (used for ASCII fast path)
+ simd8<uint8_t> prev_incomplete;
+
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+ // The only problem that can happen at EOF is that a multibyte character is too short
+ // or a byte value too large in the last bytes: check_special_cases only checks for bytes
+ // too large in the first of two bytes.
+ simdutf_really_inline void check_eof() {
+ // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't
+ // possibly finish them.
+ this->error |= this->prev_incomplete;
+ }
+
+ simdutf_really_inline void check_next_input(const simd8x64<uint8_t>& input) {
+ if(simdutf_likely(is_ascii(input))) {
+ this->error |= this->prev_incomplete;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ this->prev_incomplete = is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1]);
+ this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1];
+
+ }
+ }
+
+ // do not forget to call check_eof!
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // namespace utf8_validation
+
+using utf8_validation::utf8_checker;
+
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+/* begin file src/generic/utf8_validation/utf8_validator.h */
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_validation {
+
+/**
+ * Validates that the string is actual UTF-8.
+ */
+template<class checker>
+bool generic_validate_utf8(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ return !c.errors();
+}
+
+bool generic_validate_utf8(const char * input, size_t length) {
+ return generic_validate_utf8<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+/**
+ * Validates that the string is actual UTF-8 and stops on errors.
+ */
+template<class checker>
+result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ if(c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input + count), length - count);
+ res.count += count;
+ return res;
+ }
+ reader.advance();
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ if (c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input) + count, length - count);
+ res.count += count;
+ return res;
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_utf8_with_errors(const char * input, size_t length) {
+ return generic_validate_utf8_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+bool generic_validate_ascii(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ uint8_t blocks[64]{};
+ simd::simd8x64<uint8_t> running_or(blocks);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ running_or |= in;
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ running_or |= in;
+ return running_or.is_ascii();
+}
+
+bool generic_validate_ascii(const char * input, size_t length) {
+ return generic_validate_ascii<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ }
+ reader.advance();
+
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_ascii_with_errors(const char * input, size_t length) {
+ return generic_validate_ascii_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+} // namespace utf8_validation
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_validator.h */
+// transcoding from UTF-8 to UTF-16
+/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_utf16 {
+
+using namespace simd;
+
+template <endianness endian>
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char16_t* utf16_output) noexcept {
+ // The implementation is not specific to haswell and should be moved to the generic directory.
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ // this loop could be unrolled further. For example, we could process the mask
+ // far more than 64 bytes.
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // Slow path. We hope that the compiler will recognize that this is a slow path.
+ // Anything that is not a continuation mask is a 'leading byte', that is, the
+ // start of a new code point.
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ // The *start* of code points is not so useful, rather, we want the *end* of code points.
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times when using solely
+ // the slow/regular path, and at least four times if there are fast paths.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ //
+ // Thus we may allow convert_masked_utf8_to_utf16 to process
+ // more bytes at a time under a fast-path mode where 16 bytes
+ // are consumed at once (e.g., when encountering ASCII).
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(input + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(input + pos, size - pos, utf16_output);
+ return utf16_output - start;
+}
+
+} // namespace utf8_to_utf16
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_utf16 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+ template <endianness endian>
+ simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf16::convert<endian>(in + pos, size - pos, utf16_output);
+ if(howmany == 0) { return 0; }
+ utf16_output += howmany;
+ }
+ return utf16_output - start;
+ }
+
+ template <endianness endian>
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf16_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf16 namespace
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+// transcoding from UTF-8 to UTF-32
+/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_utf32 {
+
+using namespace simd;
+
+
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char32_t* utf32_output) noexcept {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ size_t max_starting_point = (pos + 64) - 12;
+ while(pos < max_starting_point) {
+ size_t consumed = convert_masked_utf8_to_utf32(input + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ }
+ }
+ utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output);
+ return utf32_output - start;
+}
+
+
+} // namespace utf8_to_utf32
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_utf32 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
+ if(howmany == 0) { return 0; }
+ utf32_output += howmany;
+ }
+ return utf32_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf32_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf32 namespace
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+// other functions
+/* begin file src/generic/utf8.h */
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8 {
+
+using namespace simd;
+
+simdutf_really_inline size_t count_code_points(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.gt(-65);
+ count += count_ones(utf8_continuation_mask);
+ }
+ return count + scalar::utf8::count_code_points(in + pos, size - pos);
+}
+
+simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ // We count one word for anything that is not a continuation (so
+ // leading bytes).
+ count += 64 - count_ones(utf8_continuation_mask);
+ int64_t utf8_4byte = input.gteq_unsigned(240);
+ count += count_ones(utf8_4byte);
+ }
+ return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
+}
+} // utf8 namespace
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8.h */
+/* begin file src/generic/utf16.h */
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf16 {
+
+template <endianness big_endian>
+simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
+ count += count_ones(not_pair) / 2;
+ }
+ return count + scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t ascii_mask = input.lteq(0x7F);
+ uint64_t twobyte_mask = input.lteq(0x7FF);
+ uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF);
+
+ size_t ascii_count = count_ones(ascii_mask) / 2;
+ size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2;
+ size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2;
+ size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2;
+ count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count;
+ }
+ return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) {
+ return count_code_points<big_endian>(in, size);
+}
+
+simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) {
+ size_t pos = 0;
+
+ while (pos < size/32*32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ input.swap_bytes();
+ input.store(reinterpret_cast<uint16_t *>(output));
+ pos += 32;
+ output += 32;
+ }
+
+ scalar::utf16::change_endianness_utf16(in + pos, size - pos, output);
+}
+
+} // utf16
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf16.h */
+// transcoding from UTF-8 to Latin 1
+/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte,
+// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else.
+//
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+ constexpr const uint8_t FORBIDDEN = 0xff;
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ FORBIDDEN,
+ // 1110____ ________ <three byte lead in byte 1>
+ FORBIDDEN,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ FORBIDDEN
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ FORBIDDEN,
+ // ____0101 ________
+ FORBIDDEN,
+ // ____011_ ________
+ FORBIDDEN,
+ FORBIDDEN,
+
+ // ____1___ ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ // ____1101 ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ this->error |= check_special_cases(input, prev1);
+ }
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
+ if(howmany == 0) { return 0; }
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ latin1_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, latin1_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace arm64
+} // namespace simdutf
+/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace arm64 {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output);
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ }
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace arm64
+ // namespace simdutf
+/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+// placeholder scalars
+
+//
+// Implementation-specific overrides
+//
+namespace simdutf {
+namespace arm64 {
+
+simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ if (length % 2 == 0) {
+ return arm_detect_encodings<utf8_validation::utf8_checker>(input, length);
+ } else {
+ if (implementation::validate_utf8(input, length)) {
+ return simdutf::encoding_type::UTF8;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ return arm64::utf8_validation::generic_validate_utf8(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ return arm64::utf8_validation::generic_validate_utf8_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return arm64::utf8_validation::generic_validate_ascii(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ return arm64::utf8_validation::generic_validate_ascii_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = arm_validate_utf16<endianness::LITTLE>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::LITTLE>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = arm_validate_utf16<endianness::BIG>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::BIG>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = arm_validate_utf16_with_errors<endianness::LITTLE>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::LITTLE>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = arm_validate_utf16_with_errors<endianness::BIG>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::BIG>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
+ const char32_t* tail = arm_validate_utf32le(buf, len);
+ if (tail) {
+ return scalar::utf32::validate(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+ result res = arm_validate_utf32le_with_errors(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char*, char*> ret = arm_convert_latin1_to_utf8(buf, len, utf8_output);
+ size_t converted_chars = ret.second - utf8_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = arm_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = arm_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char*, char32_t*> ret = arm_convert_latin1_to_utf32(buf, len, utf32_output);
+ size_t converted_chars = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return arm64::utf8_to_latin1::convert_valid(buf,len,latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::BIG>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size,
+ char32_t* utf32_output) const noexcept {
+ return utf8_to_utf32::convert_valid(input, size, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = arm_convert_utf16_to_latin1<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = arm_convert_utf16_to_latin1<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = arm_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = arm_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement a custom function.
+ return convert_utf16be_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement a custom function.
+ return convert_utf16le_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = arm_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = arm_convert_utf16_to_utf8<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = arm_convert_utf16_to_utf8_with_errors<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = arm_convert_utf16_to_utf8_with_errors<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16le_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16be_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = arm_convert_utf32_to_utf8(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = arm_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = arm_convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = arm_convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = arm_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = arm_convert_utf16_to_utf32_with_errors<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = arm_convert_utf32_to_latin1(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = arm_convert_utf32_to_latin1_with_errors(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = arm_convert_utf32_to_latin1(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert_valid(
+ ret.first, len - (ret.first - buf), ret.second);
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ // optimization opportunity: implement a custom function.
+ return convert_utf32_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = arm_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = arm_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = arm_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = arm_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16le(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16be(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16le_to_utf32(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16be_to_utf32(buf, len, utf32_output);
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ utf16::change_endianness_utf16(input, length, output);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept {
+ return count_utf8(buf,len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept {
+ return scalar::utf16::latin1_length_from_utf16(length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept {
+ return scalar::utf32::latin1_length_from_utf32(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept {
+ // See https://lemire.me/blog/2023/05/15/computing-the-utf-8-size-of-a-latin-1-string-quickly-arm-neon-edition/
+ // credit to Pete Cawley
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(input);
+ uint64_t result = 0;
+ const int lanes = sizeof(uint8x16_t);
+ uint8_t rem = length % lanes;
+ const uint8_t *simd_end = data + (length / lanes) * lanes;
+ const uint8x16_t threshold = vdupq_n_u8(0x80);
+ for (; data < simd_end; data += lanes) {
+ // load 16 bytes
+ uint8x16_t input_vec = vld1q_u8(data);
+ // compare to threshold (0x80)
+ uint8x16_t withhighbit = vcgeq_u8(input_vec, threshold);
+ // vertical addition
+ result -= vaddvq_s8(vreinterpretq_s8_u8(withhighbit));
+ }
+ return result + (length / lanes) * lanes + scalar::latin1::utf8_length_from_latin1((const char*)simd_end, rem);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
+}
+
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf16_length_from_latin1(length);
+}
+
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf32_length_from_latin1(length);
+}
+
+
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::utf16_length_from_utf8(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const uint32x4_t v_7f = vmovq_n_u32((uint32_t)0x7f);
+ const uint32x4_t v_7ff = vmovq_n_u32((uint32_t)0x7ff);
+ const uint32x4_t v_ffff = vmovq_n_u32((uint32_t)0xffff);
+ const uint32x4_t v_1 = vmovq_n_u32((uint32_t)0x1);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 4 <= length; pos += 4) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(input + pos));
+ const uint32x4_t ascii_bytes_bytemask = vcleq_u32(in, v_7f);
+ const uint32x4_t one_two_bytes_bytemask = vcleq_u32(in, v_7ff);
+ const uint32x4_t two_bytes_bytemask = veorq_u32(one_two_bytes_bytemask, ascii_bytes_bytemask);
+ const uint32x4_t three_bytes_bytemask = veorq_u32(vcleq_u32(in, v_ffff), one_two_bytes_bytemask);
+
+ const uint16x8_t reduced_ascii_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(ascii_bytes_bytemask, v_1));
+ const uint16x8_t reduced_two_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(two_bytes_bytemask, v_1));
+ const uint16x8_t reduced_three_bytes_bytemask = vreinterpretq_u16_u32(vandq_u32(three_bytes_bytemask, v_1));
+
+ const uint16x8_t compressed_bytemask0 = vpaddq_u16(reduced_ascii_bytes_bytemask, reduced_two_bytes_bytemask);
+ const uint16x8_t compressed_bytemask1 = vpaddq_u16(reduced_three_bytes_bytemask, reduced_three_bytes_bytemask);
+
+ size_t ascii_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask0), 0));
+ size_t two_bytes_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask0), 1));
+ size_t three_bytes_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask1), 0));
+
+ count += 16 - 3*ascii_count - 2*two_bytes_count - three_bytes_count;
+ }
+ return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const uint32x4_t v_ffff = vmovq_n_u32((uint32_t)0xffff);
+ const uint32x4_t v_1 = vmovq_n_u32((uint32_t)0x1);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 4 <= length; pos += 4) {
+ uint32x4_t in = vld1q_u32(reinterpret_cast<const uint32_t *>(input + pos));
+ const uint32x4_t surrogate_bytemask = vcgtq_u32(in, v_ffff);
+ const uint16x8_t reduced_bytemask = vreinterpretq_u16_u32(vandq_u32(surrogate_bytemask, v_1));
+ const uint16x8_t compressed_bytemask = vpaddq_u16(reduced_bytemask, reduced_bytemask);
+ size_t surrogate_count = count_ones(vgetq_lane_u64(vreinterpretq_u64_u16(compressed_bytemask), 0));
+ count += 4 + surrogate_count;
+ }
+ return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return encode_base64(output, input, length, options);
+}
+
+
+} // namespace arm64
+} // namespace simdutf
+
+/* begin file src/simdutf/arm64/end.h */
+/* end file src/simdutf/arm64/end.h */
+/* end file src/arm64/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_FALLBACK
+/* begin file src/fallback/implementation.cpp */
+/* begin file src/simdutf/fallback/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "fallback"
+// #define SIMDUTF_IMPLEMENTATION fallback
+/* end file src/simdutf/fallback/begin.h */
+
+
+
+
+
+
+
+
+
+namespace simdutf {
+namespace fallback {
+
+simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ int out = 0;
+ if(validate_utf8(input, length)) { out |= encoding_type::UTF8; }
+ if((length % 2) == 0) {
+ if(validate_utf16le(reinterpret_cast<const char16_t*>(input), length/2)) { out |= encoding_type::UTF16_LE; }
+ }
+ if((length % 4) == 0) {
+ if(validate_utf32(reinterpret_cast<const char32_t*>(input), length/4)) { out |= encoding_type::UTF32_LE; }
+ }
+
+ return out;
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ return scalar::utf8::validate(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ return scalar::utf8::validate_with_errors(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return scalar::ascii::validate(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ return scalar::ascii::validate_with_errors(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate<endianness::LITTLE>(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate<endianness::BIG>(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate_with_errors<endianness::LITTLE>(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate_with_errors<endianness::BIG>(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
+ return scalar::utf32::validate(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+ return scalar::utf32::validate_with_errors(buf, len);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::latin1_to_utf8::convert(buf,len,utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::latin1_to_utf16::convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::latin1_to_utf16::convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char * buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::latin1_to_utf32::convert(buf,len,utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf8_to_latin1::convert(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf8_to_latin1::convert_with_errors(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf8_to_latin1::convert_valid(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf8_to_utf16::convert_valid<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf8_to_utf32::convert(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf8_to_utf32::convert_with_errors(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size,
+ char32_t* utf32_output) const noexcept {
+ return scalar::utf8_to_utf32::convert_valid(input, size, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert<endianness::LITTLE>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert<endianness::BIG>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert_valid<endianness::LITTLE>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf16_to_latin1::convert_valid<endianness::BIG>(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_valid<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_valid<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf32_to_latin1::convert(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf32_to_latin1::convert_with_errors(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return scalar::utf32_to_latin1::convert_valid(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_valid<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_valid<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert<endianness::BIG>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_valid<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_valid<endianness::BIG>(buf, len, utf32_output);
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ scalar::utf16::change_endianness_utf16(input, length, output);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::count_code_points<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::count_code_points<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ return scalar::utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept {
+ return scalar::utf8::count_code_points(buf,len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept {
+ return scalar::utf16::latin1_length_from_utf16(length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept {
+ return length;
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept {
+ return scalar::latin1::utf8_length_from_latin1(input,length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf16_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ return scalar::utf8::utf16_length_from_utf8(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ return scalar::utf32::utf8_length_from_utf32(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ return scalar::utf32::utf16_length_from_utf32(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf32_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return scalar::utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ size_t equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ size_t equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return scalar::base64::tail_encode_base64(output, input, length, options);
+}
+} // namespace fallback
+} // namespace simdutf
+
+/* begin file src/simdutf/fallback/end.h */
+/* end file src/simdutf/fallback/end.h */
+/* end file src/fallback/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_ICELAKE
+/* begin file src/icelake/implementation.cpp */
+
+
+/* begin file src/simdutf/icelake/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "icelake"
+// #define SIMDUTF_IMPLEMENTATION icelake
+
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+// nothing needed.
+#else
+SIMDUTF_TARGET_ICELAKE
+#endif
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized)
+#endif // end of workaround
+/* end file src/simdutf/icelake/begin.h */
+namespace simdutf {
+namespace icelake {
+namespace {
+#ifndef SIMDUTF_ICELAKE_H
+#error "icelake.h must be included"
+#endif
+/* begin file src/icelake/icelake_utf8_common.inl.cpp */
+// Common procedures for both validating and non-validating conversions from UTF-8.
+enum block_processing_mode { SIMDUTF_FULL, SIMDUTF_TAIL};
+
+using utf8_to_utf16_result = std::pair<const char*, char16_t*>;
+using utf8_to_utf32_result = std::pair<const char*, uint32_t*>;
+
+/*
+ process_block_utf8_to_utf16 converts up to 64 bytes from 'in' from UTF-8
+ to UTF-16. When tail = SIMDUTF_FULL, then the full input buffer (64 bytes)
+ might be used. When tail = SIMDUTF_TAIL, we take into account 'gap' which
+ indicates how many input bytes are relevant.
+
+ Returns true when the result is correct, otherwise it returns false.
+
+ The provided in and out pointers are advanced according to how many input
+ bytes have been processed, upon success.
+*/
+template <block_processing_mode tail, endianness big_endian>
+simdutf_really_inline bool process_block_utf8_to_utf16(const char *&in, char16_t *&out, size_t gap) {
+ // constants
+ __m512i mask_identity = _mm512_set_epi8(63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0);
+ __m512i mask_c0c0c0c0 = _mm512_set1_epi32(0xc0c0c0c0);
+ __m512i mask_80808080 = _mm512_set1_epi32(0x80808080);
+ __m512i mask_f0f0f0f0 = _mm512_set1_epi32(0xf0f0f0f0);
+ __m512i mask_dfdfdfdf_tail = _mm512_set_epi64(0xffffdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf, 0xdfdfdfdfdfdfdfdf);
+ __m512i mask_c2c2c2c2 = _mm512_set1_epi32(0xc2c2c2c2);
+ __m512i mask_ffffffff = _mm512_set1_epi32(0xffffffff);
+ __m512i mask_d7c0d7c0 = _mm512_set1_epi32(0xd7c0d7c0);
+ __m512i mask_dc00dc00 = _mm512_set1_epi32(0xdc00dc00);
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ // Note that 'tail' is a compile-time constant !
+ __mmask64 b = (tail == SIMDUTF_FULL) ? 0xFFFFFFFFFFFFFFFF : (uint64_t(1) << gap) - 1;
+ __m512i input = (tail == SIMDUTF_FULL) ? _mm512_loadu_si512(in) : _mm512_maskz_loadu_epi8(b, in);
+ __mmask64 m1 = (tail == SIMDUTF_FULL) ? _mm512_cmplt_epu8_mask(input, mask_80808080) : _mm512_mask_cmplt_epu8_mask(b, input, mask_80808080);
+ if(_ktestc_mask64_u8(m1, b)) {// NOT(m1) AND b -- if all zeroes, then all ASCII
+ // alternatively, we could do 'if (m1 == b) { '
+ if (tail == SIMDUTF_FULL) {
+ in += 64; // consumed 64 bytes
+ // we convert a full 64-byte block, writing 128 bytes.
+ __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input));
+ if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); }
+ _mm512_storeu_si512(out, input1);
+ out += 32;
+ __m512i input2 = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(input, 1));
+ if(big_endian) { input2 = _mm512_shuffle_epi8(input2, byteflip); }
+ _mm512_storeu_si512(out, input2);
+ out += 32;
+ return true; // we are done
+ } else {
+ in += gap;
+ if (gap <= 32) {
+ __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input));
+ if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); }
+ _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << (gap)) - 1), input1);
+ out += gap;
+ } else {
+ __m512i input1 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(input));
+ if(big_endian) { input1 = _mm512_shuffle_epi8(input1, byteflip); }
+ _mm512_storeu_si512(out, input1);
+ out += 32;
+ __m512i input2 = _mm512_cvtepu8_epi16(_mm512_extracti64x4_epi64(input, 1));
+ if(big_endian) { input2 = _mm512_shuffle_epi8(input2, byteflip); }
+ _mm512_mask_storeu_epi16(out, __mmask32((uint32_t(1) << (gap - 32)) - 1), input2);
+ out += gap - 32;
+ }
+ return true; // we are done
+ }
+ }
+ // classify characters further
+ __mmask64 m234 = _mm512_cmp_epu8_mask(mask_c0c0c0c0, input,
+ _MM_CMPINT_LE); // 0xc0 <= input, 2, 3, or 4 leading byte
+ __mmask64 m34 = _mm512_cmp_epu8_mask(mask_dfdfdfdf_tail, input,
+ _MM_CMPINT_LT); // 0xdf < input, 3 or 4 leading byte
+
+ __mmask64 milltwobytes = _mm512_mask_cmp_epu8_mask(m234, input, mask_c2c2c2c2,
+ _MM_CMPINT_LT); // 0xc0 <= input < 0xc2 (illegal two byte sequence)
+ // Overlong 2-byte sequence
+ if (_ktestz_mask64_u8(milltwobytes, milltwobytes) == 0) {
+ // Overlong 2-byte sequence
+ return false;
+ }
+ if (_ktestz_mask64_u8(m34, m34) == 0) {
+ // We have a 3-byte sequence and/or a 2-byte sequence, or possibly even a 4-byte sequence!
+ __mmask64 m4 = _mm512_cmp_epu8_mask(input, mask_f0f0f0f0,
+ _MM_CMPINT_NLT); // 0xf0 <= zmm0 (4 byte start bytes)
+
+ __mmask64 mask_not_ascii = (tail == SIMDUTF_FULL) ? _knot_mask64(m1) : _kand_mask64(_knot_mask64(m1), b);
+
+ __mmask64 mp1 = _kshiftli_mask64(m234, 1);
+ __mmask64 mp2 = _kshiftli_mask64(m34, 2);
+ // We could do it as follows...
+ // if (_kortestz_mask64_u8(m4,m4)) { // compute the bitwise OR of the 64-bit masks a and b and return 1 if all zeroes
+ // but GCC generates better code when we do:
+ if (m4 == 0) { // compute the bitwise OR of the 64-bit masks a and b and return 1 if all zeroes
+ // Fast path with 1,2,3 bytes
+ __mmask64 mc = _kor_mask64(mp1, mp2); // expected continuation bytes
+ __mmask64 m1234 = _kor_mask64(m1, m234);
+ // mismatched continuation bytes:
+ if (tail == SIMDUTF_FULL) {
+ __mmask64 xnormcm1234 = _kxnor_mask64(mc, m1234); // XNOR of mc and m1234 should be all zero if they differ
+ // the presence of a 1 bit indicates that they overlap.
+ // _kortestz_mask64_u8: compute the bitwise OR of 64-bit masksand return 1 if all zeroes.
+ if (!_kortestz_mask64_u8(xnormcm1234, xnormcm1234)) { return false; }
+ } else {
+ __mmask64 bxorm1234 = _kxor_mask64(b, m1234);
+ if (mc != bxorm1234) { return false; }
+ }
+ // mend: identifying the last bytes of each sequence to be decoded
+ __mmask64 mend = _kshiftri_mask64(m1234, 1);
+ if (tail != SIMDUTF_FULL) {
+ mend = _kor_mask64(mend, (uint64_t(1) << (gap - 1)));
+ }
+
+
+ __m512i last_and_third = _mm512_maskz_compress_epi8(mend, mask_identity);
+ __m512i last_and_thirdu16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(last_and_third));
+
+ __m512i nonasciitags = _mm512_maskz_mov_epi8(mask_not_ascii, mask_c0c0c0c0); // ASCII: 00000000 other: 11000000
+ __m512i clearedbytes = _mm512_andnot_si512(nonasciitags, input); // high two bits cleared where not ASCII
+ __m512i lastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, last_and_thirdu16,
+ clearedbytes); // the last byte of each character
+
+ __mmask64 mask_before_non_ascii = _kshiftri_mask64(mask_not_ascii, 1); // bytes that precede non-ASCII bytes
+ __m512i indexofsecondlastbytes = _mm512_add_epi16(mask_ffffffff, last_and_thirdu16); // indices of the second last bytes
+ __m512i beforeasciibytes = _mm512_maskz_mov_epi8(mask_before_non_ascii, clearedbytes);
+ __m512i secondlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofsecondlastbytes,
+ beforeasciibytes); // the second last bytes (of two, three byte seq,
+ // surrogates)
+ secondlastbytes = _mm512_slli_epi16(secondlastbytes, 6); // shifted into position
+
+ __m512i indexofthirdlastbytes = _mm512_add_epi16(mask_ffffffff,
+ indexofsecondlastbytes); // indices of the second last bytes
+ __m512i thirdlastbyte = _mm512_maskz_mov_epi8(m34,
+ clearedbytes); // only those that are the third last byte of a sequence
+ __m512i thirdlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofthirdlastbytes,
+ thirdlastbyte); // the third last bytes (of three byte sequences, hi
+ // surrogate)
+ thirdlastbytes = _mm512_slli_epi16(thirdlastbytes, 12); // shifted into position
+ __m512i Wout = _mm512_ternarylogic_epi32(lastbytes, secondlastbytes, thirdlastbytes, 254);
+ // the elements of Wout excluding the last element if it happens to be a high surrogate:
+
+ __mmask64 mprocessed = (tail == SIMDUTF_FULL) ? _pdep_u64(0xFFFFFFFF, mend) : _pdep_u64(0xFFFFFFFF, _kand_mask64(mend, b)); // we adjust mend at the end of the output.
+
+
+ // Encodings out of range...
+ {
+ // the location of 3-byte sequence start bytes in the input
+ __mmask64 m3 = m34 & (b ^ m4);
+ // code units in Wout corresponding to 3-byte sequences.
+ __mmask32 M3 = __mmask32(_pext_u64(m3 << 2, mend));
+ __m512i mask_08000800 = _mm512_set1_epi32(0x08000800);
+ __mmask32 Msmall800 = _mm512_mask_cmplt_epu16_mask(M3, Wout, mask_08000800);
+ __m512i mask_d800d800 = _mm512_set1_epi32(0xd800d800);
+ __m512i Moutminusd800 = _mm512_sub_epi16(Wout, mask_d800d800);
+ __mmask32 M3s = _mm512_mask_cmplt_epu16_mask(M3, Moutminusd800, mask_08000800);
+ if (_kor_mask32(Msmall800, M3s)) { return false; }
+ }
+ int64_t nout = _mm_popcnt_u64(mprocessed);
+ in += 64 - _lzcnt_u64(mprocessed);
+ if(big_endian) { Wout = _mm512_shuffle_epi8(Wout, byteflip); }
+ _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), Wout);
+ out += nout;
+ return true; // ok
+ }
+ //
+ // We have a 4-byte sequence, this is the general case.
+ // Slow!
+ __mmask64 mp3 = _kshiftli_mask64(m4, 3);
+ __mmask64 mc = _kor_mask64(_kor_mask64(mp1, mp2), mp3); // expected continuation bytes
+ __mmask64 m1234 = _kor_mask64(m1, m234);
+
+ // mend: identifying the last bytes of each sequence to be decoded
+ __mmask64 mend = _kor_mask64(_kshiftri_mask64(_kor_mask64(mp3, m1234), 1), mp3);
+ if (tail != SIMDUTF_FULL) {
+ mend = _kor_mask64(mend, __mmask64(uint64_t(1) << (gap - 1)));
+ }
+ __m512i last_and_third = _mm512_maskz_compress_epi8(mend, mask_identity);
+ __m512i last_and_thirdu16 = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(last_and_third));
+
+ __m512i nonasciitags = _mm512_maskz_mov_epi8(mask_not_ascii, mask_c0c0c0c0); // ASCII: 00000000 other: 11000000
+ __m512i clearedbytes = _mm512_andnot_si512(nonasciitags, input); // high two bits cleared where not ASCII
+ __m512i lastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, last_and_thirdu16,
+ clearedbytes); // the last byte of each character
+
+ __mmask64 mask_before_non_ascii = _kshiftri_mask64(mask_not_ascii, 1); // bytes that precede non-ASCII bytes
+ __m512i indexofsecondlastbytes = _mm512_add_epi16(mask_ffffffff, last_and_thirdu16); // indices of the second last bytes
+ __m512i beforeasciibytes = _mm512_maskz_mov_epi8(mask_before_non_ascii, clearedbytes);
+ __m512i secondlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofsecondlastbytes,
+ beforeasciibytes); // the second last bytes (of two, three byte seq,
+ // surrogates)
+ secondlastbytes = _mm512_slli_epi16(secondlastbytes, 6); // shifted into position
+
+ __m512i indexofthirdlastbytes = _mm512_add_epi16(mask_ffffffff,
+ indexofsecondlastbytes); // indices of the second last bytes
+ __m512i thirdlastbyte = _mm512_maskz_mov_epi8(m34,
+ clearedbytes); // only those that are the third last byte of a sequence
+ __m512i thirdlastbytes = _mm512_maskz_permutexvar_epi8(0x5555555555555555, indexofthirdlastbytes,
+ thirdlastbyte); // the third last bytes (of three byte sequences, hi
+ // surrogate)
+ thirdlastbytes = _mm512_slli_epi16(thirdlastbytes, 12); // shifted into position
+ __m512i thirdsecondandlastbytes = _mm512_ternarylogic_epi32(lastbytes, secondlastbytes, thirdlastbytes, 254);
+ uint64_t Mlo_uint64 = _pext_u64(mp3, mend);
+ __mmask32 Mlo = __mmask32(Mlo_uint64);
+ __mmask32 Mhi = __mmask32(Mlo_uint64 >> 1);
+ __m512i lo_surr_mask = _mm512_maskz_mov_epi16(Mlo,
+ mask_dc00dc00); // lo surr: 1101110000000000, other: 0000000000000000
+ __m512i shifted4_thirdsecondandlastbytes = _mm512_srli_epi16(thirdsecondandlastbytes,
+ 4); // hi surr: 00000WVUTSRQPNML vuts = WVUTS - 1
+ __m512i tagged_lo_surrogates = _mm512_or_si512(thirdsecondandlastbytes,
+ lo_surr_mask); // lo surr: 110111KJHGFEDCBA, other: unchanged
+ __m512i Wout = _mm512_mask_add_epi16(tagged_lo_surrogates, Mhi, shifted4_thirdsecondandlastbytes,
+ mask_d7c0d7c0); // hi sur: 110110vutsRQPNML, other: unchanged
+ // the elements of Wout excluding the last element if it happens to be a high surrogate:
+ __mmask32 Mout = ~(Mhi & 0x80000000);
+ __mmask64 mprocessed = (tail == SIMDUTF_FULL) ? _pdep_u64(Mout, mend) : _pdep_u64(Mout, _kand_mask64(mend, b)); // we adjust mend at the end of the output.
+
+
+ // mismatched continuation bytes:
+ if (tail == SIMDUTF_FULL) {
+ __mmask64 xnormcm1234 = _kxnor_mask64(mc, m1234); // XNOR of mc and m1234 should be all zero if they differ
+ // the presence of a 1 bit indicates that they overlap.
+ // _kortestz_mask64_u8: compute the bitwise OR of 64-bit masksand return 1 if all zeroes.
+ if (!_kortestz_mask64_u8(xnormcm1234, xnormcm1234)) { return false; }
+ } else {
+ __mmask64 bxorm1234 = _kxor_mask64(b, m1234);
+ if (mc != bxorm1234) { return false; }
+ }
+ // Encodings out of range...
+ {
+ // the location of 3-byte sequence start bytes in the input
+ __mmask64 m3 = m34 & (b ^ m4);
+ // code units in Wout corresponding to 3-byte sequences.
+ __mmask32 M3 = __mmask32(_pext_u64(m3 << 2, mend));
+ __m512i mask_08000800 = _mm512_set1_epi32(0x08000800);
+ __mmask32 Msmall800 = _mm512_mask_cmplt_epu16_mask(M3, Wout, mask_08000800);
+ __m512i mask_d800d800 = _mm512_set1_epi32(0xd800d800);
+ __m512i Moutminusd800 = _mm512_sub_epi16(Wout, mask_d800d800);
+ __mmask32 M3s = _mm512_mask_cmplt_epu16_mask(M3, Moutminusd800, mask_08000800);
+ __m512i mask_04000400 = _mm512_set1_epi32(0x04000400);
+ __mmask32 M4s = _mm512_mask_cmpge_epu16_mask(Mhi, Moutminusd800, mask_04000400);
+ if (!_kortestz_mask32_u8(M4s, _kor_mask32(Msmall800, M3s))) { return false; }
+ }
+ in += 64 - _lzcnt_u64(mprocessed);
+ int64_t nout = _mm_popcnt_u64(mprocessed);
+ if(big_endian) { Wout = _mm512_shuffle_epi8(Wout, byteflip); }
+ _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), Wout);
+ out += nout;
+ return true; // ok
+ }
+ // Fast path 2: all ASCII or 2 byte
+ __mmask64 continuation_or_ascii = (tail == SIMDUTF_FULL) ? _knot_mask64(m234) : _kand_mask64(_knot_mask64(m234), b);
+ // on top of -0xc0 we subtract -2 which we get back later of the
+ // continuation byte tags
+ __m512i leading2byte = _mm512_maskz_sub_epi8(m234, input, mask_c2c2c2c2);
+ __mmask64 leading = tail == (tail == SIMDUTF_FULL) ? _kor_mask64(m1, m234) : _kand_mask64(_kor_mask64(m1, m234), b); // first bytes of each sequence
+ if (tail == SIMDUTF_FULL) {
+ __mmask64 xnor234leading = _kxnor_mask64(_kshiftli_mask64(m234, 1), leading);
+ if (!_kortestz_mask64_u8(xnor234leading, xnor234leading)) { return false; }
+ } else {
+ __mmask64 bxorleading = _kxor_mask64(b, leading);
+ if (_kshiftli_mask64(m234, 1) != bxorleading) { return false; }
+ }
+ //
+ if (tail == SIMDUTF_FULL) {
+ // In the two-byte/ASCII scenario, we are easily latency bound, so we want
+ // to increment the input buffer as quickly as possible.
+ // We process 32 bytes unless the byte at index 32 is a continuation byte,
+ // in which case we include it as well for a total of 33 bytes.
+ // Note that if x is an ASCII byte, then the following is false:
+ // int8_t(x) <= int8_t(0xc0) under two's complement.
+ in += 32;
+ if(int8_t(*in) <= int8_t(0xc0)) in++;
+ // The alternative is to do
+ // in += 64 - _lzcnt_u64(_pdep_u64(0xFFFFFFFF, continuation_or_ascii));
+ // but it requires loading the input, doing the mask computation, and converting
+ // back the mask to a general register. It just takes too long, leaving the
+ // processor likely to be idle.
+ } else {
+ in += 64 - _lzcnt_u64(_pdep_u64(0xFFFFFFFF, continuation_or_ascii));
+ }
+ __m512i lead = _mm512_maskz_compress_epi8(leading, leading2byte); // will contain zero for ascii, and the data
+ lead = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(lead)); // ... zero extended into code units
+ __m512i follow = _mm512_maskz_compress_epi8(continuation_or_ascii, input); // the last bytes of each sequence
+ follow = _mm512_cvtepu8_epi16(_mm512_castsi512_si256(follow)); // ... zero extended into code units
+ lead = _mm512_slli_epi16(lead, 6); // shifted into position
+ __m512i final = _mm512_add_epi16(follow, lead); // combining lead and follow
+
+ if(big_endian) { final = _mm512_shuffle_epi8(final, byteflip); }
+ if (tail == SIMDUTF_FULL) {
+ // Next part is UTF-16 specific and can be generalized to UTF-32.
+ int nout = _mm_popcnt_u32(uint32_t(leading));
+ _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), final);
+ out += nout; // UTF-8 to UTF-16 is only expansionary in this case.
+ } else {
+ int nout = int(_mm_popcnt_u64(_pdep_u64(0xFFFFFFFF, leading)));
+ _mm512_mask_storeu_epi16(out, __mmask32((uint64_t(1) << nout) - 1), final);
+ out += nout; // UTF-8 to UTF-16 is only expansionary in this case.
+ }
+
+ return true; // we are fine.
+}
+
+
+
+
+/*
+ utf32_to_utf16_masked converts `count` lower UTF-32 code units
+ from input `utf32` into UTF-16. It differs from utf32_to_utf16
+ in that it 'masks' the writes.
+
+ Returns how many 16-bit code units were stored.
+
+ byteflip is used for flipping 16-bit code units, and it should be
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ We pass it to the (always inlined) function to encourage the compiler to
+ keep the value in a (constant) register.
+*/
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_to_utf16_masked(const __m512i byteflip, __m512i utf32, unsigned int count, char16_t* output) {
+
+ const __mmask16 valid = uint16_t((1 << count) - 1);
+ // 1. check if we have any surrogate pairs
+ const __m512i v_0000_ffff = _mm512_set1_epi32(0x0000ffff);
+ const __mmask16 sp_mask = _mm512_mask_cmpgt_epu32_mask(valid, utf32, v_0000_ffff);
+
+ if (sp_mask == 0) {
+ if(big_endian) {
+ _mm256_mask_storeu_epi16((__m256i*)output, valid, _mm256_shuffle_epi8(_mm512_cvtepi32_epi16(utf32), _mm512_castsi512_si256(byteflip)));
+
+ } else {
+ _mm256_mask_storeu_epi16((__m256i*)output, valid, _mm512_cvtepi32_epi16(utf32));
+ }
+ return count;
+ }
+
+ {
+ // build surrogate pair code units in 32-bit lanes
+
+ // t0 = 8 x [000000000000aaaa|aaaaaabbbbbbbbbb]
+ const __m512i v_0001_0000 = _mm512_set1_epi32(0x00010000);
+ const __m512i t0 = _mm512_sub_epi32(utf32, v_0001_0000);
+
+ // t1 = 8 x [000000aaaaaaaaaa|bbbbbbbbbb000000]
+ const __m512i t1 = _mm512_slli_epi32(t0, 6);
+
+ // t2 = 8 x [000000aaaaaaaaaa|aaaaaabbbbbbbbbb] -- copy hi word from t1 to t0
+ // 0xe4 = (t1 and v_ffff_0000) or (t0 and not v_ffff_0000)
+ const __m512i v_ffff_0000 = _mm512_set1_epi32(0xffff0000);
+ const __m512i t2 = _mm512_ternarylogic_epi32(t1, t0, v_ffff_0000, 0xe4);
+
+ // t2 = 8 x [110110aaaaaaaaaa|110111bbbbbbbbbb] -- copy hi word from t1 to t0
+ // 0xba = (t2 and not v_fc00_fc000) or v_d800_dc00
+ const __m512i v_fc00_fc00 = _mm512_set1_epi32(0xfc00fc00);
+ const __m512i v_d800_dc00 = _mm512_set1_epi32(0xd800dc00);
+ const __m512i t3 = _mm512_ternarylogic_epi32(t2, v_fc00_fc00, v_d800_dc00, 0xba);
+ const __m512i t4 = _mm512_mask_blend_epi32(sp_mask, utf32, t3);
+ __m512i t5 = _mm512_ror_epi32(t4, 16);
+ // Here we want to trim all of the upper 16-bit code units from the 2-byte
+ // characters represented as 4-byte values. We can compute it from
+ // sp_mask or the following... It can be more optimized!
+ const __mmask32 nonzero = _kor_mask32(0xaaaaaaaa,_mm512_cmpneq_epi16_mask(t5, _mm512_setzero_si512()));
+ const __mmask32 nonzero_masked = _kand_mask32(nonzero, __mmask32((uint64_t(1) << (2*count)) - 1));
+ if(big_endian) { t5 = _mm512_shuffle_epi8(t5, byteflip); }
+ // we deliberately avoid _mm512_mask_compressstoreu_epi16 for portability (zen4)
+ __m512i compressed = _mm512_maskz_compress_epi16(nonzero_masked, t5);
+ _mm512_mask_storeu_epi16(output, (1<<(count + static_cast<unsigned int>(count_ones(sp_mask)))) - 1, compressed);
+ //_mm512_mask_compressstoreu_epi16(output, nonzero_masked, t5);
+ }
+
+ return count + static_cast<unsigned int>(count_ones(sp_mask));
+}
+
+/*
+ utf32_to_utf16 converts `count` lower UTF-32 code units
+ from input `utf32` into UTF-16. It may overflow.
+
+ Returns how many 16-bit code units were stored.
+
+ byteflip is used for flipping 16-bit code units, and it should be
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ We pass it to the (always inlined) function to encourage the compiler to
+ keep the value in a (constant) register.
+*/
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_to_utf16(const __m512i byteflip, __m512i utf32, unsigned int count, char16_t* output) {
+ // check if we have any surrogate pairs
+ const __m512i v_0000_ffff = _mm512_set1_epi32(0x0000ffff);
+ const __mmask16 sp_mask = _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff);
+
+ if (sp_mask == 0) {
+ // technically, it should be _mm256_storeu_epi16
+ if(big_endian) {
+ _mm256_storeu_si256((__m256i*)output, _mm256_shuffle_epi8(_mm512_cvtepi32_epi16(utf32),_mm512_castsi512_si256(byteflip)));
+ } else {
+ _mm256_storeu_si256((__m256i*)output, _mm512_cvtepi32_epi16(utf32));
+ }
+ return count;
+ }
+
+ {
+ // build surrogate pair code units in 32-bit lanes
+
+ // t0 = 8 x [000000000000aaaa|aaaaaabbbbbbbbbb]
+ const __m512i v_0001_0000 = _mm512_set1_epi32(0x00010000);
+ const __m512i t0 = _mm512_sub_epi32(utf32, v_0001_0000);
+
+ // t1 = 8 x [000000aaaaaaaaaa|bbbbbbbbbb000000]
+ const __m512i t1 = _mm512_slli_epi32(t0, 6);
+
+ // t2 = 8 x [000000aaaaaaaaaa|aaaaaabbbbbbbbbb] -- copy hi word from t1 to t0
+ // 0xe4 = (t1 and v_ffff_0000) or (t0 and not v_ffff_0000)
+ const __m512i v_ffff_0000 = _mm512_set1_epi32(0xffff0000);
+ const __m512i t2 = _mm512_ternarylogic_epi32(t1, t0, v_ffff_0000, 0xe4);
+
+ // t2 = 8 x [110110aaaaaaaaaa|110111bbbbbbbbbb] -- copy hi word from t1 to t0
+ // 0xba = (t2 and not v_fc00_fc000) or v_d800_dc00
+ const __m512i v_fc00_fc00 = _mm512_set1_epi32(0xfc00fc00);
+ const __m512i v_d800_dc00 = _mm512_set1_epi32(0xd800dc00);
+ const __m512i t3 = _mm512_ternarylogic_epi32(t2, v_fc00_fc00, v_d800_dc00, 0xba);
+ const __m512i t4 = _mm512_mask_blend_epi32(sp_mask, utf32, t3);
+ __m512i t5 = _mm512_ror_epi32(t4, 16);
+ const __mmask32 nonzero = _kor_mask32(0xaaaaaaaa,_mm512_cmpneq_epi16_mask(t5, _mm512_setzero_si512()));
+ if(big_endian) { t5 = _mm512_shuffle_epi8(t5, byteflip); }
+ // we deliberately avoid _mm512_mask_compressstoreu_epi16 for portability (zen4)
+ __m512i compressed = _mm512_maskz_compress_epi16(nonzero, t5);
+ _mm512_mask_storeu_epi16(output, (1<<(count + static_cast<unsigned int>(count_ones(sp_mask)))) - 1, compressed);
+ //_mm512_mask_compressstoreu_epi16(output, nonzero, t5);
+ }
+
+ return count + static_cast<unsigned int>(count_ones(sp_mask));
+}
+
+/**
+ * Store the last N bytes of previous followed by 512-N bytes from input.
+ */
+template <int N>
+__m512i prev(__m512i input, __m512i previous) {
+ static_assert(N<=32, "N must be no larger than 32");
+ const __m512i movemask = _mm512_setr_epi32(28,29,30,31,0,1,2,3,4,5,6,7,8,9,10,11);
+ const __m512i rotated = _mm512_permutex2var_epi32(input, movemask, previous);
+#if SIMDUTF_GCC8 || SIMDUTF_GCC9
+ constexpr int shift = 16-N; // workaround for GCC8,9
+ return _mm512_alignr_epi8(input, rotated, shift);
+#else
+ return _mm512_alignr_epi8(input, rotated, 16-N);
+#endif // SIMDUTF_GCC8 || SIMDUTF_GCC9
+}
+
+template <unsigned idx0, unsigned idx1, unsigned idx2, unsigned idx3>
+__m512i shuffle_epi128(__m512i v) {
+ static_assert((idx0 >= 0 && idx0 <= 3), "idx0 must be in range 0..3");
+ static_assert((idx1 >= 0 && idx1 <= 3), "idx1 must be in range 0..3");
+ static_assert((idx2 >= 0 && idx2 <= 3), "idx2 must be in range 0..3");
+ static_assert((idx3 >= 0 && idx3 <= 3), "idx3 must be in range 0..3");
+
+ constexpr unsigned shuffle = idx0 | (idx1 << 2) | (idx2 << 4) | (idx3 << 6);
+ return _mm512_shuffle_i32x4(v, v, shuffle);
+}
+
+template <unsigned idx>
+constexpr __m512i broadcast_epi128(__m512i v) {
+ return shuffle_epi128<idx, idx, idx, idx>(v);
+}
+
+/**
+ * Current unused.
+ */
+template <int N>
+__m512i rotate_by_N_epi8(const __m512i input) {
+
+ // lanes order: 1, 2, 3, 0 => 0b00_11_10_01
+ const __m512i permuted = _mm512_shuffle_i32x4(input, input, 0x39);
+
+ return _mm512_alignr_epi8(permuted, input, N);
+}
+
+/*
+ expanded_utf8_to_utf32 converts expanded UTF-8 characters (`utf8`)
+ stored at separate 32-bit lanes.
+
+ For each lane we have also a character class (`char_class), given in form
+ 0x8080800N, where N is 4 highest bits from the leading byte; 0x80 resets
+ corresponding bytes during pshufb.
+*/
+simdutf_really_inline __m512i expanded_utf8_to_utf32(__m512i char_class, __m512i utf8) {
+ /*
+ Input:
+ - utf8: bytes stored at separate 32-bit code units
+ - valid: which code units have valid UTF-8 characters
+
+ Bit layout of single word. We show 4 cases for each possible
+ UTF-8 character encoding. The `?` denotes bits we must not
+ assume their value.
+
+ |10dd.dddd|10cc.cccc|10bb.bbbb|1111.0aaa| 4-byte char
+ |????.????|10cc.cccc|10bb.bbbb|1110.aaaa| 3-byte char
+ |????.????|????.????|10bb.bbbb|110a.aaaa| 2-byte char
+ |????.????|????.????|????.????|0aaa.aaaa| ASCII char
+ byte 3 byte 2 byte 1 byte 0
+ */
+
+ /* 1. Reset control bits of continuation bytes and the MSB
+ of the leading byte; this makes all bytes unsigned (and
+ does not alter ASCII char).
+
+ |00dd.dddd|00cc.cccc|00bb.bbbb|0111.0aaa| 4-byte char
+ |00??.????|00cc.cccc|00bb.bbbb|0110.aaaa| 3-byte char
+ |00??.????|00??.????|00bb.bbbb|010a.aaaa| 2-byte char
+ |00??.????|00??.????|00??.????|0aaa.aaaa| ASCII char
+ ^^ ^^ ^^ ^
+ */
+ __m512i values;
+ const __m512i v_3f3f_3f7f = _mm512_set1_epi32(0x3f3f3f7f);
+ values = _mm512_and_si512(utf8, v_3f3f_3f7f);
+
+ /* 2. Swap and join fields A-B and C-D
+
+ |0000.cccc|ccdd.dddd|0001.110a|aabb.bbbb| 4-byte char
+ |0000.cccc|cc??.????|0001.10aa|aabb.bbbb| 3-byte char
+ |0000.????|????.????|0001.0aaa|aabb.bbbb| 2-byte char
+ |0000.????|????.????|000a.aaaa|aa??.????| ASCII char */
+ const __m512i v_0140_0140 = _mm512_set1_epi32(0x01400140);
+ values = _mm512_maddubs_epi16(values, v_0140_0140);
+
+ /* 3. Swap and join fields AB & CD
+
+ |0000.0001|110a.aabb|bbbb.cccc|ccdd.dddd| 4-byte char
+ |0000.0001|10aa.aabb|bbbb.cccc|cc??.????| 3-byte char
+ |0000.0001|0aaa.aabb|bbbb.????|????.????| 2-byte char
+ |0000.000a|aaaa.aa??|????.????|????.????| ASCII char */
+ const __m512i v_0001_1000 = _mm512_set1_epi32(0x00011000);
+ values = _mm512_madd_epi16(values, v_0001_1000);
+
+ /* 4. Shift left the values by variable amounts to reset highest UTF-8 bits
+ |aaab.bbbb|bccc.cccd|dddd.d000|0000.0000| 4-byte char -- by 11
+ |aaaa.bbbb|bbcc.cccc|????.??00|0000.0000| 3-byte char -- by 10
+ |aaaa.abbb|bbb?.????|????.???0|0000.0000| 2-byte char -- by 9
+ |aaaa.aaa?|????.????|????.????|?000.0000| ASCII char -- by 7 */
+ {
+ /** pshufb
+
+ continuation = 0
+ ascii = 7
+ _2_bytes = 9
+ _3_bytes = 10
+ _4_bytes = 11
+
+ shift_left_v3 = 4 * [
+ ascii, # 0000
+ ascii, # 0001
+ ascii, # 0010
+ ascii, # 0011
+ ascii, # 0100
+ ascii, # 0101
+ ascii, # 0110
+ ascii, # 0111
+ continuation, # 1000
+ continuation, # 1001
+ continuation, # 1010
+ continuation, # 1011
+ _2_bytes, # 1100
+ _2_bytes, # 1101
+ _3_bytes, # 1110
+ _4_bytes, # 1111
+ ] */
+ const __m512i shift_left_v3 = _mm512_setr_epi64(
+ 0x0707070707070707,
+ 0x0b0a090900000000,
+ 0x0707070707070707,
+ 0x0b0a090900000000,
+ 0x0707070707070707,
+ 0x0b0a090900000000,
+ 0x0707070707070707,
+ 0x0b0a090900000000
+ );
+
+ const __m512i shift = _mm512_shuffle_epi8(shift_left_v3, char_class);
+ values = _mm512_sllv_epi32(values, shift);
+ }
+
+ /* 5. Shift right the values by variable amounts to reset lowest bits
+ |0000.0000|000a.aabb|bbbb.cccc|ccdd.dddd| 4-byte char -- by 11
+ |0000.0000|0000.0000|aaaa.bbbb|bbcc.cccc| 3-byte char -- by 16
+ |0000.0000|0000.0000|0000.0aaa|aabb.bbbb| 2-byte char -- by 21
+ |0000.0000|0000.0000|0000.0000|0aaa.aaaa| ASCII char -- by 25 */
+ {
+ // 4 * [25, 25, 25, 25, 25, 25, 25, 25, 0, 0, 0, 0, 21, 21, 16, 11]
+ const __m512i shift_right = _mm512_setr_epi64(
+ 0x1919191919191919,
+ 0x0b10151500000000,
+ 0x1919191919191919,
+ 0x0b10151500000000,
+ 0x1919191919191919,
+ 0x0b10151500000000,
+ 0x1919191919191919,
+ 0x0b10151500000000
+ );
+
+ const __m512i shift = _mm512_shuffle_epi8(shift_right, char_class);
+ values = _mm512_srlv_epi32(values, shift);
+ }
+
+ return values;
+}
+
+
+simdutf_really_inline __m512i expand_and_identify(__m512i lane0, __m512i lane1, int &count) {
+ const __m512i merged = _mm512_mask_mov_epi32(lane0, 0x1000, lane1);
+ const __m512i expand_ver2 = _mm512_setr_epi64(
+ 0x0403020103020100,
+ 0x0605040305040302,
+ 0x0807060507060504,
+ 0x0a09080709080706,
+ 0x0c0b0a090b0a0908,
+ 0x0e0d0c0b0d0c0b0a,
+ 0x000f0e0d0f0e0d0c,
+ 0x0201000f01000f0e
+ );
+ const __m512i input = _mm512_shuffle_epi8(merged, expand_ver2);
+ const __m512i v_0000_00c0 = _mm512_set1_epi32(0xc0);
+ const __m512i t0 = _mm512_and_si512(input, v_0000_00c0);
+ const __m512i v_0000_0080 = _mm512_set1_epi32(0x80);
+ const __mmask16 leading_bytes = _mm512_cmpneq_epu32_mask(t0, v_0000_0080);
+ count = static_cast<int>(count_ones(leading_bytes));
+ return _mm512_mask_compress_epi32(_mm512_setzero_si512(), leading_bytes, input);
+}
+
+simdutf_really_inline __m512i expand_utf8_to_utf32(__m512i input) {
+ __m512i char_class = _mm512_srli_epi32(input, 4);
+ /* char_class = ((input >> 4) & 0x0f) | 0x80808000 */
+ const __m512i v_0000_000f = _mm512_set1_epi32(0x0f);
+ const __m512i v_8080_8000 = _mm512_set1_epi32(0x80808000);
+ char_class = _mm512_ternarylogic_epi32(char_class, v_0000_000f, v_8080_8000, 0xea);
+ return expanded_utf8_to_utf32(char_class, input);
+}
+/* end file src/icelake/icelake_utf8_common.inl.cpp */
+/* begin file src/icelake/icelake_macros.inl.cpp */
+
+/*
+ This upcoming macro (SIMDUTF_ICELAKE_TRANSCODE16) takes 16 + 4 bytes (of a UTF-8 string)
+ and loads all possible 4-byte substring into an AVX512 register.
+
+ For example if we have bytes abcdefgh... we create following 32-bit lanes
+
+ [abcd|bcde|cdef|defg|efgh|...]
+ ^ ^
+ byte 0 of reg byte 63 of reg
+*/
+/** pshufb
+ # lane{0,1,2} have got bytes: [ 0, 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15]
+ # lane3 has got bytes: [ 16, 17, 18, 19, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15]
+
+ expand_ver2 = [
+ # lane 0:
+ 0, 1, 2, 3,
+ 1, 2, 3, 4,
+ 2, 3, 4, 5,
+ 3, 4, 5, 6,
+
+ # lane 1:
+ 4, 5, 6, 7,
+ 5, 6, 7, 8,
+ 6, 7, 8, 9,
+ 7, 8, 9, 10,
+
+ # lane 2:
+ 8, 9, 10, 11,
+ 9, 10, 11, 12,
+ 10, 11, 12, 13,
+ 11, 12, 13, 14,
+
+ # lane 3 order: 13, 14, 15, 16 14, 15, 16, 17, 15, 16, 17, 18, 16, 17, 18, 19
+ 12, 13, 14, 15,
+ 13, 14, 15, 0,
+ 14, 15, 0, 1,
+ 15, 0, 1, 2,
+ ]
+*/
+
+#define SIMDUTF_ICELAKE_TRANSCODE16(LANE0, LANE1, MASKED) \
+ { \
+ const __m512i merged = _mm512_mask_mov_epi32(LANE0, 0x1000, LANE1); \
+ const __m512i expand_ver2 = _mm512_setr_epi64( \
+ 0x0403020103020100, \
+ 0x0605040305040302, \
+ 0x0807060507060504, \
+ 0x0a09080709080706, \
+ 0x0c0b0a090b0a0908, \
+ 0x0e0d0c0b0d0c0b0a, \
+ 0x000f0e0d0f0e0d0c, \
+ 0x0201000f01000f0e \
+ ); \
+ const __m512i input = _mm512_shuffle_epi8(merged, expand_ver2); \
+ \
+ __mmask16 leading_bytes; \
+ const __m512i v_0000_00c0 = _mm512_set1_epi32(0xc0); \
+ const __m512i t0 = _mm512_and_si512(input, v_0000_00c0); \
+ const __m512i v_0000_0080 = _mm512_set1_epi32(0x80); \
+ leading_bytes = _mm512_cmpneq_epu32_mask(t0, v_0000_0080); \
+ \
+ __m512i char_class; \
+ char_class = _mm512_srli_epi32(input, 4); \
+ /* char_class = ((input >> 4) & 0x0f) | 0x80808000 */ \
+ const __m512i v_0000_000f = _mm512_set1_epi32(0x0f); \
+ const __m512i v_8080_8000 = _mm512_set1_epi32(0x80808000); \
+ char_class = _mm512_ternarylogic_epi32(char_class, v_0000_000f, v_8080_8000, 0xea); \
+ \
+ const int valid_count = static_cast<int>(count_ones(leading_bytes)); \
+ const __m512i utf32 = expanded_utf8_to_utf32(char_class, input); \
+ \
+ const __m512i out = _mm512_mask_compress_epi32(_mm512_setzero_si512(), leading_bytes, utf32); \
+ \
+ if (UTF32) { \
+ if(MASKED) { \
+ const __mmask16 valid = uint16_t((1 << valid_count) - 1); \
+ _mm512_mask_storeu_epi32((__m512i*)output, valid, out); \
+ } else { \
+ _mm512_storeu_si512((__m512i*)output, out); \
+ } \
+ output += valid_count; \
+ } else { \
+ if(MASKED) { \
+ output += utf32_to_utf16_masked<big_endian>(byteflip, out, valid_count, reinterpret_cast<char16_t *>(output)); \
+ } else { \
+ output += utf32_to_utf16<big_endian>(byteflip, out, valid_count, reinterpret_cast<char16_t *>(output)); \
+ } \
+ } \
+ }
+
+#define SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(INPUT, VALID_COUNT, MASKED) \
+{ \
+ if (UTF32) { \
+ if(MASKED) { \
+ const __mmask16 valid_mask = uint16_t((1 << VALID_COUNT) - 1); \
+ _mm512_mask_storeu_epi32((__m512i*)output, valid_mask, INPUT); \
+ } else { \
+ _mm512_storeu_si512((__m512i*)output, INPUT); \
+ } \
+ output += VALID_COUNT; \
+ } else { \
+ if(MASKED) { \
+ output += utf32_to_utf16_masked<big_endian>(byteflip, INPUT, VALID_COUNT, reinterpret_cast<char16_t *>(output)); \
+ } else { \
+ output += utf32_to_utf16<big_endian>(byteflip, INPUT, VALID_COUNT, reinterpret_cast<char16_t *>(output)); \
+ } \
+ } \
+}
+
+
+#define SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output) \
+ if (UTF32) { \
+ const __m128i t0 = _mm512_castsi512_si128(utf8); \
+ const __m128i t1 = _mm512_extracti32x4_epi32(utf8, 1); \
+ const __m128i t2 = _mm512_extracti32x4_epi32(utf8, 2); \
+ const __m128i t3 = _mm512_extracti32x4_epi32(utf8, 3); \
+ _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_cvtepu8_epi32(t0)); \
+ _mm512_storeu_si512((__m512i*)(output + 1*16), _mm512_cvtepu8_epi32(t1)); \
+ _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_cvtepu8_epi32(t2)); \
+ _mm512_storeu_si512((__m512i*)(output + 3*16), _mm512_cvtepu8_epi32(t3)); \
+ } else { \
+ const __m256i h0 = _mm512_castsi512_si256(utf8); \
+ const __m256i h1 = _mm512_extracti64x4_epi64(utf8, 1); \
+ if(big_endian) { \
+ _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_shuffle_epi8(_mm512_cvtepu8_epi16(h0), byteflip)); \
+ _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_shuffle_epi8(_mm512_cvtepu8_epi16(h1), byteflip)); \
+ } else { \
+ _mm512_storeu_si512((__m512i*)(output + 0*16), _mm512_cvtepu8_epi16(h0)); \
+ _mm512_storeu_si512((__m512i*)(output + 2*16), _mm512_cvtepu8_epi16(h1)); \
+ } \
+ }
+/* end file src/icelake/icelake_macros.inl.cpp */
+/* begin file src/icelake/icelake_from_valid_utf8.inl.cpp */
+// file included directly
+
+// File contains conversion procedure from VALID UTF-8 strings.
+
+/*
+ valid_utf8_to_fixed_length converts a valid UTF-8 string into UTF-32.
+
+ The `OUTPUT` template type decides what to do with UTF-32: store
+ it directly or convert into UTF-16 (with AVX512).
+
+ Input:
+ - str - valid UTF-8 string
+ - len - string length
+ - out_buffer - output buffer
+
+ Result:
+ - pair.first - the first unprocessed input byte
+ - pair.second - the first unprocessed output word
+*/
+template <endianness big_endian, typename OUTPUT>
+std::pair<const char*, OUTPUT*> valid_utf8_to_fixed_length(const char* str, size_t len, OUTPUT* dwords) {
+ constexpr bool UTF32 = std::is_same<OUTPUT, uint32_t>::value;
+ constexpr bool UTF16 = std::is_same<OUTPUT, char16_t>::value;
+ static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)");
+ static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32");
+
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ const char* ptr = str;
+ const char* end = ptr + len;
+
+ OUTPUT* output = dwords;
+ /**
+ * In the main loop, we consume 64 bytes per iteration,
+ * but we access 64 + 4 bytes.
+ * We check for ptr + 64 + 64 <= end because
+ * we want to be do maskless writes without overruns.
+ */
+ while (ptr + 64 + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ const __m512i v_80 = _mm512_set1_epi8(char(0x80));
+ const __mmask64 ascii = _mm512_test_epi8_mask(utf8, v_80);
+ if(ascii == 0) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ continue;
+ }
+
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, false)
+ }
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ int valid_count2;
+ __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2);
+ uint32_t tmp1;
+ ::memcpy(&tmp1, ptr + 64, sizeof(tmp1));
+ const __m512i lane4 = _mm512_set1_epi32(tmp1);
+ int valid_count3;
+ __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3);
+ if(valid_count2 + valid_count3 <= 16) {
+ vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<<valid_count3)-1)<<valid_count2), vec3);
+ valid_count2 += valid_count3;
+ vec2 = expand_utf8_to_utf32(vec2);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ } else {
+ vec2 = expand_utf8_to_utf32(vec2);
+ vec3 = expand_utf8_to_utf32(vec3);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec3, valid_count3, false)
+ }
+ ptr += 4*16;
+ }
+
+ if (ptr + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ const __m512i v_80 = _mm512_set1_epi8(char(0x80));
+ const __mmask64 ascii = _mm512_test_epi8_mask(utf8, v_80);
+ if(ascii == 0) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ } else {
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, true)
+ }
+
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true)
+
+ ptr += 3*16;
+ }
+ }
+ return {ptr, output};
+}
+
+
+using utf8_to_utf16_result = std::pair<const char*, char16_t*>;
+/* end file src/icelake/icelake_from_valid_utf8.inl.cpp */
+/* begin file src/icelake/icelake_utf8_validation.inl.cpp */
+// file included directly
+
+
+simdutf_really_inline __m512i check_special_cases(__m512i input, const __m512i prev1) {
+ __m512i mask1 = _mm512_setr_epi64(
+ 0x0202020202020202,
+ 0x4915012180808080,
+ 0x0202020202020202,
+ 0x4915012180808080,
+ 0x0202020202020202,
+ 0x4915012180808080,
+ 0x0202020202020202,
+ 0x4915012180808080);
+ const __m512i v_0f = _mm512_set1_epi8(0x0f);
+ __m512i index1 = _mm512_and_si512(_mm512_srli_epi16(prev1, 4), v_0f);
+
+ __m512i byte_1_high = _mm512_shuffle_epi8(mask1, index1);
+ __m512i mask2 = _mm512_setr_epi64(
+ 0xcbcbcb8b8383a3e7,
+ 0xcbcbdbcbcbcbcbcb,
+ 0xcbcbcb8b8383a3e7,
+ 0xcbcbdbcbcbcbcbcb,
+ 0xcbcbcb8b8383a3e7,
+ 0xcbcbdbcbcbcbcbcb,
+ 0xcbcbcb8b8383a3e7,
+ 0xcbcbdbcbcbcbcbcb);
+ __m512i index2 = _mm512_and_si512(prev1, v_0f);
+
+ __m512i byte_1_low = _mm512_shuffle_epi8(mask2, index2);
+ __m512i mask3 = _mm512_setr_epi64(
+ 0x101010101010101,
+ 0x1010101babaaee6,
+ 0x101010101010101,
+ 0x1010101babaaee6,
+ 0x101010101010101,
+ 0x1010101babaaee6,
+ 0x101010101010101,
+ 0x1010101babaaee6
+ );
+ __m512i index3 = _mm512_and_si512(_mm512_srli_epi16(input, 4), v_0f);
+ __m512i byte_2_high = _mm512_shuffle_epi8(mask3, index3);
+ return _mm512_ternarylogic_epi64(byte_1_high, byte_1_low, byte_2_high, 128);
+ }
+
+ simdutf_really_inline __m512i check_multibyte_lengths(const __m512i input,
+ const __m512i prev_input, const __m512i sc) {
+ __m512i prev2 = prev<2>(input, prev_input);
+ __m512i prev3 = prev<3>(input, prev_input);
+ __m512i is_third_byte = _mm512_subs_epu8(prev2, _mm512_set1_epi8(0b11100000u-1)); // Only 111_____ will be > 0
+ __m512i is_fourth_byte = _mm512_subs_epu8(prev3, _mm512_set1_epi8(0b11110000u-1)); // Only 1111____ will be > 0
+ __m512i is_third_or_fourth_byte = _mm512_or_si512(is_third_byte, is_fourth_byte);
+ const __m512i v_7f = _mm512_set1_epi8(char(0x7f));
+ is_third_or_fourth_byte = _mm512_adds_epu8(v_7f, is_third_or_fourth_byte);
+ // We want to compute (is_third_or_fourth_byte AND v80) XOR sc.
+ const __m512i v_80 = _mm512_set1_epi8(char(0x80));
+ return _mm512_ternarylogic_epi32(is_third_or_fourth_byte, v_80, sc, 0b1101010);
+ //__m512i is_third_or_fourth_byte_mask = _mm512_and_si512(is_third_or_fourth_byte, v_80);
+ //return _mm512_xor_si512(is_third_or_fourth_byte_mask, sc);
+ }
+ //
+ // Return nonzero if there are incomplete multibyte characters at the end of the block:
+ // e.g. if there is a 4-byte character, but it's 3 bytes from the end.
+ //
+ simdutf_really_inline __m512i is_incomplete(const __m512i input) {
+ // If the previous input's last 3 bytes match this, they're too short (they ended at EOF):
+ // ... 1111____ 111_____ 11______
+ __m512i max_value = _mm512_setr_epi64(
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xffffffffffffffff,
+ 0xbfdfefffffffffff);
+ return _mm512_subs_epu8(input, max_value);
+ }
+
+ struct avx512_utf8_checker {
+ // If this is nonzero, there has been a UTF-8 error.
+ __m512i error{};
+
+ // The last input we received
+ __m512i prev_input_block{};
+ // Whether the last input we received was incomplete (used for ASCII fast path)
+ __m512i prev_incomplete{};
+
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const __m512i input, const __m512i prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ __m512i prev1 = prev<1>(input, prev_input);
+ __m512i sc = check_special_cases(input, prev1);
+ this->error = _mm512_or_si512(check_multibyte_lengths(input, prev_input, sc), this->error);
+ }
+
+ // The only problem that can happen at EOF is that a multibyte character is too short
+ // or a byte value too large in the last bytes: check_special_cases only checks for bytes
+ // too large in the first of two bytes.
+ simdutf_really_inline void check_eof() {
+ // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't
+ // possibly finish them.
+ this->error = _mm512_or_si512(this->error, this->prev_incomplete);
+ }
+
+ // returns true if ASCII.
+ simdutf_really_inline bool check_next_input(const __m512i input) {
+ const __m512i v_80 = _mm512_set1_epi8(char(0x80));
+ const __mmask64 ascii = _mm512_test_epi8_mask(input, v_80);
+ if(ascii == 0) {
+ this->error = _mm512_or_si512(this->error, this->prev_incomplete);
+ return true;
+ } else {
+ this->check_utf8_bytes(input, this->prev_input_block);
+ this->prev_incomplete = is_incomplete(input);
+ this->prev_input_block = input;
+ return false;
+ }
+ }
+ // do not forget to call check_eof!
+ simdutf_really_inline bool errors() const {
+ return _mm512_test_epi8_mask(this->error, this->error) != 0;
+ }
+
+ }; // struct avx512_utf8_checker
+/* end file src/icelake/icelake_utf8_validation.inl.cpp */
+/* begin file src/icelake/icelake_from_utf8.inl.cpp */
+// file included directly
+
+// File contains conversion procedure from possibly invalid UTF-8 strings.
+
+/**
+ * Attempts to convert up to len 1-byte code units from in (in UTF-8 format) to
+ * out.
+ * Returns the position of the input and output after the processing is
+ * completed. Upon error, the output is set to null.
+ */
+
+template <endianness big_endian>
+utf8_to_utf16_result fast_avx512_convert_utf8_to_utf16(const char *in, size_t len, char16_t *out) {
+ const char *const final_in = in + len;
+ bool result = true;
+ while (result) {
+ if (in + 64 <= final_in) {
+ result = process_block_utf8_to_utf16<SIMDUTF_FULL, big_endian>(in, out, final_in - in);
+ } else if(in < final_in) {
+ result = process_block_utf8_to_utf16<SIMDUTF_TAIL, big_endian>(in, out, final_in - in);
+ } else { break; }
+ }
+ if(!result) { out = nullptr; }
+ return std::make_pair(in, out);
+}
+
+template <endianness big_endian>
+simdutf::result fast_avx512_convert_utf8_to_utf16_with_errors(const char *in, size_t len, char16_t *out) {
+ const char *const init_in = in;
+ const char16_t *const init_out = out;
+ const char *const final_in = in + len;
+ bool result = true;
+ while (result) {
+ if (in + 64 <= final_in) {
+ result = process_block_utf8_to_utf16<SIMDUTF_FULL, big_endian>(in, out, final_in - in);
+ } else if(in < final_in) {
+ result = process_block_utf8_to_utf16<SIMDUTF_TAIL, big_endian>(in, out, final_in - in);
+ } else { break; }
+ }
+ if(!result) {
+ // rewind_and_convert_with_errors will seek a potential error from in onward,
+ // with the ability to go back up to in - init_in bytes, and read final_in - in bytes forward.
+ simdutf::result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<big_endian>(in - init_in, in, final_in - in, out);
+ res.count += (in - init_in);
+ return res;
+ } else {
+ return simdutf::result(error_code::SUCCESS,out - init_out);
+ }
+}
+
+
+template <endianness big_endian, typename OUTPUT>
+std::pair<const char*, OUTPUT*> validating_utf8_to_fixed_length(const char* str, size_t len, OUTPUT* dwords) {
+ constexpr bool UTF32 = std::is_same<OUTPUT, uint32_t>::value;
+ constexpr bool UTF16 = std::is_same<OUTPUT, char16_t>::value;
+ static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)");
+ static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32");
+
+ const char* ptr = str;
+ const char* end = ptr + len;
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ OUTPUT* output = dwords;
+ avx512_utf8_checker checker{};
+ /**
+ * In the main loop, we consume 64 bytes per iteration,
+ * but we access 64 + 4 bytes.
+ * We check for ptr + 64 + 64 <= end because
+ * we want to be do maskless writes without overruns.
+ */
+ while (ptr + 64 + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ if(checker.check_next_input(utf8)) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ continue;
+ }
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, false)
+ }
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ int valid_count2;
+ __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2);
+ uint32_t tmp1;
+ ::memcpy(&tmp1, ptr + 64, sizeof(tmp1));
+ const __m512i lane4 = _mm512_set1_epi32(tmp1);
+ int valid_count3;
+ __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3);
+ if(valid_count2 + valid_count3 <= 16) {
+ vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<<valid_count3)-1)<<valid_count2), vec3);
+ valid_count2 += valid_count3;
+ vec2 = expand_utf8_to_utf32(vec2);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ } else {
+ vec2 = expand_utf8_to_utf32(vec2);
+ vec3 = expand_utf8_to_utf32(vec3);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec3, valid_count3, false)
+ }
+ ptr += 4*16;
+ }
+ const char* validatedptr = ptr; // validated up to ptr
+
+ // For the final pass, we validate 64 bytes, but we only transcode
+ // 3*16 bytes, so we may end up double-validating 16 bytes.
+ if (ptr + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ if(checker.check_next_input(utf8)) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ } else {
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, true)
+ }
+
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true)
+
+ ptr += 3*16;
+ }
+ validatedptr += 4*16;
+ }
+ {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - validatedptr))-1, (const __m512i*)validatedptr);
+ checker.check_next_input(utf8);
+ }
+ checker.check_eof();
+ if(checker.errors()) {
+ return {ptr, nullptr}; // We found an error.
+ }
+ return {ptr, output};
+}
+
+// Like validating_utf8_to_fixed_length but returns as soon as an error is identified
+template <endianness big_endian, typename OUTPUT>
+std::tuple<const char*, OUTPUT*, bool> validating_utf8_to_fixed_length_with_constant_checks(const char* str, size_t len, OUTPUT* dwords) {
+ constexpr bool UTF32 = std::is_same<OUTPUT, uint32_t>::value;
+ constexpr bool UTF16 = std::is_same<OUTPUT, char16_t>::value;
+ static_assert(UTF32 or UTF16, "output type has to be uint32_t (for UTF-32) or char16_t (for UTF-16)");
+ static_assert(!(UTF32 and big_endian), "we do not currently support big-endian UTF-32");
+
+ const char* ptr = str;
+ const char* end = ptr + len;
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ OUTPUT* output = dwords;
+ avx512_utf8_checker checker{};
+ /**
+ * In the main loop, we consume 64 bytes per iteration,
+ * but we access 64 + 4 bytes.
+ * We check for ptr + 64 + 64 <= end because
+ * we want to be do maskless writes without overruns.
+ */
+ while (ptr + 64 + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ if(checker.check_next_input(utf8)) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ continue;
+ }
+ if(checker.errors()) {
+ return {ptr, output, false}; // We found an error.
+ }
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, false)
+ }
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ int valid_count2;
+ __m512i vec2 = expand_and_identify(lane2, lane3, valid_count2);
+ uint32_t tmp1;
+ ::memcpy(&tmp1, ptr + 64, sizeof(tmp1));
+ const __m512i lane4 = _mm512_set1_epi32(tmp1);
+ int valid_count3;
+ __m512i vec3 = expand_and_identify(lane3, lane4, valid_count3);
+ if(valid_count2 + valid_count3 <= 16) {
+ vec2 = _mm512_mask_expand_epi32(vec2, __mmask16(((1<<valid_count3)-1)<<valid_count2), vec3);
+ valid_count2 += valid_count3;
+ vec2 = expand_utf8_to_utf32(vec2);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ } else {
+ vec2 = expand_utf8_to_utf32(vec2);
+ vec3 = expand_utf8_to_utf32(vec3);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec2, valid_count2, false)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec3, valid_count3, false)
+ }
+ ptr += 4*16;
+ }
+ const char* validatedptr = ptr; // validated up to ptr
+
+ // For the final pass, we validate 64 bytes, but we only transcode
+ // 3*16 bytes, so we may end up double-validating 16 bytes.
+ if (ptr + 64 <= end) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ if(checker.check_next_input(utf8)) {
+ SIMDUTF_ICELAKE_STORE_ASCII(UTF32, utf8, output)
+ output += 64;
+ ptr += 64;
+ } else if(checker.errors()) {
+ return {ptr, output, false}; // We found an error.
+ } else {
+ const __m512i lane0 = broadcast_epi128<0>(utf8);
+ const __m512i lane1 = broadcast_epi128<1>(utf8);
+ int valid_count0;
+ __m512i vec0 = expand_and_identify(lane0, lane1, valid_count0);
+ const __m512i lane2 = broadcast_epi128<2>(utf8);
+ int valid_count1;
+ __m512i vec1 = expand_and_identify(lane1, lane2, valid_count1);
+ if(valid_count0 + valid_count1 <= 16) {
+ vec0 = _mm512_mask_expand_epi32(vec0, __mmask16(((1<<valid_count1)-1)<<valid_count0), vec1);
+ valid_count0 += valid_count1;
+ vec0 = expand_utf8_to_utf32(vec0);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ } else {
+ vec0 = expand_utf8_to_utf32(vec0);
+ vec1 = expand_utf8_to_utf32(vec1);
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec0, valid_count0, true)
+ SIMDUTF_ICELAKE_WRITE_UTF16_OR_UTF32(vec1, valid_count1, true)
+ }
+
+ const __m512i lane3 = broadcast_epi128<3>(utf8);
+ SIMDUTF_ICELAKE_TRANSCODE16(lane2, lane3, true)
+
+ ptr += 3*16;
+ }
+ validatedptr += 4*16;
+ }
+ {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - validatedptr))-1, (const __m512i*)validatedptr);
+ checker.check_next_input(utf8);
+ }
+ checker.check_eof();
+ if(checker.errors()) {
+ return {ptr, output, false}; // We found an error.
+ }
+ return {ptr, output, true};
+}
+/* end file src/icelake/icelake_from_utf8.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf8_to_latin1.inl.cpp */
+// file included directly
+
+// File contains conversion procedure from possibly invalid UTF-8 strings.
+
+// template <bool is_remaining, bool use_masked_store>
+template <bool is_remaining>
+simdutf_really_inline size_t process_block_from_utf8_to_latin1(const char *buf, size_t len,
+ char *latin_output, __m512i minus64,
+ __m512i one,
+ __mmask64 *next_leading_ptr,
+ __mmask64 *next_bit6_ptr) {
+ __mmask64 load_mask =
+ is_remaining ? _bzhi_u64(~0ULL, (unsigned int)len) : ~0ULL;
+ __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)buf);
+ __mmask64 nonascii = _mm512_movepi8_mask(input);
+
+ if (nonascii == 0) {
+ is_remaining
+ ? _mm512_mask_storeu_epi8((__m512i *)latin_output, load_mask, input)
+ : _mm512_storeu_si512((__m512i *)latin_output, input);
+ return len;
+ }
+
+ __mmask64 leading = _mm512_cmpge_epu8_mask(input, minus64);
+
+ __m512i highbits = _mm512_xor_si512(input, _mm512_set1_epi8(-62));
+ __mmask64 invalid_leading_bytes =
+ _mm512_mask_cmpgt_epu8_mask(leading, highbits, one);
+
+ if (invalid_leading_bytes) {
+ return 0; // Indicates error
+ }
+
+ __mmask64 leading_shift = (leading << 1) | *next_leading_ptr;
+ *next_leading_ptr = leading >> 63;
+
+ if ((nonascii ^ leading) != leading_shift) {
+ return 0; // Indicates error
+ }
+
+ __mmask64 bit6 = _mm512_cmpeq_epi8_mask(highbits, one);
+ input =
+ _mm512_mask_sub_epi8(input, (bit6 << 1) | *next_bit6_ptr, input, minus64);
+ *next_bit6_ptr = bit6 >> 63;
+
+ __mmask64 retain = ~leading & load_mask;
+ __m512i output = _mm512_maskz_compress_epi8(retain, input);
+ int64_t written_out = count_ones(retain);
+ __mmask64 store_mask = (1ULL << written_out) - 1;
+
+ // ***************************
+ // Possible optimization? (Nick Nuon)
+ // This commented out line is 5% faster but sadly it'll also write past
+ // memory bounds for latin1_output: is_remaining ?
+ // _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output) :
+ // _mm512_storeu_si512((__m512i *)latin_output, output); I tried using
+ // _mm512_storeu_si512 and have the next process_block start from the
+ // "written_out" point but the compiler shuffles memory in such a way that it
+ // is significantly slower...
+ // ****************************
+ _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output);
+
+ return written_out;
+}
+
+size_t utf8_to_latin1_avx512(const char *buf, size_t len, char *latin_output) {
+ char *start = latin_output;
+ size_t pos = 0;
+ __m512i minus64 = _mm512_set1_epi8(-64); // 11111111111 ... 1100 0000
+ __m512i one = _mm512_set1_epi8(1);
+ __mmask64 next_leading = 0;
+ __mmask64 next_bit6 = 0;
+
+ while (pos + 64 <= len) {
+ size_t written = process_block_from_utf8_to_latin1<false>(buf + pos, 64, latin_output, minus64,
+ one, &next_leading, &next_bit6);
+ if (written == 0) {
+ return 0; // Indicates error
+ }
+ latin_output += written;
+ pos += 64;
+ }
+
+ if (pos < len) {
+ size_t remaining = len - pos;
+ size_t written =
+ process_block_from_utf8_to_latin1<true>(buf + pos, remaining, latin_output, minus64, one,
+ &next_leading, &next_bit6);
+ if (written == 0) {
+ return 0; // Indicates error
+ }
+ latin_output += written;
+ }
+
+ return (size_t)(latin_output - start);
+}
+/* end file src/icelake/icelake_convert_utf8_to_latin1.inl.cpp */
+/* begin file src/icelake/icelake_convert_valid_utf8_to_latin1.inl.cpp */
+// file included directly
+
+// File contains conversion procedure from valid UTF-8 strings.
+
+template <bool is_remaining>
+simdutf_really_inline size_t process_valid_block_from_utf8_to_latin1(const char *buf, size_t len,
+ char *latin_output,
+ __m512i minus64, __m512i one,
+ __mmask64 *next_leading_ptr,
+ __mmask64 *next_bit6_ptr) {
+ __mmask64 load_mask =
+ is_remaining ? _bzhi_u64(~0ULL, (unsigned int)len) : ~0ULL;
+ __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)buf);
+ __mmask64 nonascii = _mm512_movepi8_mask(input);
+
+ if (nonascii == 0) {
+ is_remaining
+ ? _mm512_mask_storeu_epi8((__m512i *)latin_output, load_mask, input)
+ : _mm512_storeu_si512((__m512i *)latin_output, input);
+ return len;
+ }
+
+ __mmask64 leading = _mm512_cmpge_epu8_mask(input, minus64);
+
+ __m512i highbits = _mm512_xor_si512(input, _mm512_set1_epi8(-62));
+
+ *next_leading_ptr = leading >> 63;
+
+ __mmask64 bit6 = _mm512_cmpeq_epi8_mask(highbits, one);
+ input =
+ _mm512_mask_sub_epi8(input, (bit6 << 1) | *next_bit6_ptr, input, minus64);
+ *next_bit6_ptr = bit6 >> 63;
+
+ __mmask64 retain = ~leading & load_mask;
+ __m512i output = _mm512_maskz_compress_epi8(retain, input);
+ int64_t written_out = count_ones(retain);
+ __mmask64 store_mask = (1ULL << written_out) - 1;
+ // Optimization opportunity: sometimes, masked writes are not needed.
+ _mm512_mask_storeu_epi8((__m512i *)latin_output, store_mask, output);
+ return written_out;
+}
+
+size_t valid_utf8_to_latin1_avx512(const char *buf, size_t len,
+ char *latin_output) {
+ char *start = latin_output;
+ size_t pos = 0;
+ __m512i minus64 = _mm512_set1_epi8(-64); // 11111111111 ... 1100 0000
+ __m512i one = _mm512_set1_epi8(1);
+ __mmask64 next_leading = 0;
+ __mmask64 next_bit6 = 0;
+
+ while (pos + 64 <= len) {
+ size_t written = process_valid_block_from_utf8_to_latin1<false>(
+ buf + pos, 64, latin_output, minus64, one, &next_leading, &next_bit6);
+ latin_output += written;
+ pos += 64;
+ }
+
+ if (pos < len) {
+ size_t remaining = len - pos;
+ size_t written =
+ process_valid_block_from_utf8_to_latin1<true>(buf + pos, remaining, latin_output, minus64,
+ one, &next_leading, &next_bit6);
+ latin_output += written;
+ }
+
+ return (size_t)(latin_output - start);
+}
+/* end file src/icelake/icelake_convert_valid_utf8_to_latin1.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf16_to_latin1.inl.cpp */
+// file included directly
+template <endianness big_endian>
+size_t icelake_convert_utf16_to_latin1(const char16_t *buf, size_t len,
+ char *latin1_output) {
+ const char16_t *end = buf + len;
+ __m512i v_0xFF = _mm512_set1_epi16(0xff);
+ __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809);
+ __m512i shufmask = _mm512_set_epi8(
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0);
+ while (buf + 32 <= end) {
+ __m512i in = _mm512_loadu_si512((__m512i *)buf);
+ if (big_endian) {
+ in = _mm512_shuffle_epi8(in, byteflip);
+ }
+ if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) {
+ return 0;
+ }
+ _mm256_storeu_si256(
+ (__m256i *)latin1_output,
+ _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in)));
+ latin1_output += 32;
+ buf += 32;
+ }
+ if (buf < end) {
+ uint32_t mask(uint32_t(1 << (end - buf)) - 1);
+ __m512i in = _mm512_maskz_loadu_epi16(mask, buf);
+ if (big_endian) {
+ in = _mm512_shuffle_epi8(in, byteflip);
+ }
+ if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) {
+ return 0;
+ }
+ _mm256_mask_storeu_epi8(
+ latin1_output, mask,
+ _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in)));
+ }
+ return len;
+}
+
+template <endianness big_endian>
+std::pair<result, char *>
+icelake_convert_utf16_to_latin1_with_errors(const char16_t *buf, size_t len,
+ char *latin1_output) {
+ const char16_t *end = buf + len;
+ const char16_t *start = buf;
+ __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809);
+ __m512i v_0xFF = _mm512_set1_epi16(0xff);
+ __m512i shufmask = _mm512_set_epi8(
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 62, 60, 58, 56, 54, 52, 50, 48, 46, 44, 42, 40, 38,
+ 36, 34, 32, 30, 28, 26, 24, 22, 20, 18, 16, 14, 12, 10, 8, 6, 4, 2, 0);
+ while (buf + 32 <= end) {
+ __m512i in = _mm512_loadu_si512((__m512i *)buf);
+ if (big_endian) {
+ in = _mm512_shuffle_epi8(in, byteflip);
+ }
+ if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) {
+ uint16_t word;
+ while ((word = (big_endian ? scalar::utf16::swap_bytes(uint16_t(*buf))
+ : uint16_t(*buf))) <= 0xff) {
+ *latin1_output++ = uint8_t(word);
+ buf++;
+ }
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start),
+ latin1_output);
+ }
+ _mm256_storeu_si256(
+ (__m256i *)latin1_output,
+ _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in)));
+ latin1_output += 32;
+ buf += 32;
+ }
+ if (buf < end) {
+ uint32_t mask(uint32_t(1 << (end - buf)) - 1);
+ __m512i in = _mm512_maskz_loadu_epi16(mask, buf);
+ if (big_endian) {
+ in = _mm512_shuffle_epi8(in, byteflip);
+ }
+ if (_mm512_cmpgt_epu16_mask(in, v_0xFF)) {
+
+ uint16_t word;
+ while ((word = (big_endian ? scalar::utf16::swap_bytes(uint16_t(*buf))
+ : uint16_t(*buf))) <= 0xff) {
+ *latin1_output++ = uint8_t(word);
+ buf++;
+ }
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start),
+ latin1_output);
+ }
+ _mm256_mask_storeu_epi8(
+ latin1_output, mask,
+ _mm512_castsi512_si256(_mm512_permutexvar_epi8(shufmask, in)));
+ }
+ return std::make_pair(result(error_code::SUCCESS, len), latin1_output);
+}
+/* end file src/icelake/icelake_convert_utf16_to_latin1.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf16_to_utf8.inl.cpp */
+// file included directly
+
+/**
+ * This function converts the input (inbuf, inlen), assumed to be valid
+ * UTF16 (little endian) into UTF-8 (to outbuf). The number of code units written
+ * is written to 'outlen' and the function reports the number of input word
+ * consumed.
+ */
+template <endianness big_endian>
+size_t utf16_to_utf8_avx512i(const char16_t *inbuf, size_t inlen,
+ unsigned char *outbuf, size_t *outlen) {
+ __m512i in;
+ __mmask32 inmask = _cvtu32_mask32(0x7fffffff);
+ __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ const char16_t * const inbuf_orig = inbuf;
+ const unsigned char * const outbuf_orig = outbuf;
+ size_t adjust = 0;
+ int carry = 0;
+
+ while (inlen >= 32) {
+ in = _mm512_loadu_si512(inbuf);
+ if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); }
+ inlen -= 31;
+ lastiteration:
+ inbuf += 31;
+
+ failiteration:
+ const __mmask32 is234byte = _mm512_mask_cmp_epu16_mask(
+ inmask, in, _mm512_set1_epi16(0x0080), _MM_CMPINT_NLT);
+
+ if (_ktestz_mask32_u8(inmask, is234byte)) {
+ // fast path for ASCII only
+ _mm512_mask_cvtepi16_storeu_epi8(outbuf, inmask, in);
+ outbuf += 31;
+ carry = 0;
+
+ if (inlen < 32) {
+ goto tail;
+ } else {
+ continue;
+ }
+ }
+
+ const __mmask32 is12byte =
+ _mm512_cmp_epu16_mask(in, _mm512_set1_epi16(0x0800), _MM_CMPINT_LT);
+
+ if (_ktestc_mask32_u8(is12byte, inmask)) {
+ // fast path for 1 and 2 byte only
+
+ const __m512i twobytes = _mm512_ternarylogic_epi32(
+ _mm512_slli_epi16(in, 8), _mm512_srli_epi16(in, 6),
+ _mm512_set1_epi16(0x3f3f), 0xa8); // (A|B)&C
+ in = _mm512_mask_add_epi16(in, is234byte, twobytes,
+ _mm512_set1_epi16(int16_t(0x80c0)));
+ const __m512i cmpmask =
+ _mm512_mask_blend_epi16(inmask, _mm512_set1_epi16(int16_t(0xffff)),
+ _mm512_set1_epi16(0x0800));
+ const __mmask64 smoosh = _mm512_cmp_epu8_mask(in, cmpmask, _MM_CMPINT_NLT);
+ const __m512i out = _mm512_maskz_compress_epi8(smoosh, in);
+ _mm512_mask_storeu_epi8(outbuf, _cvtu64_mask64(_pext_u64(_cvtmask64_u64(smoosh), _cvtmask64_u64(smoosh))),
+ out);
+ outbuf += 31 + _mm_popcnt_u32(_cvtmask32_u32(is234byte));
+ carry = 0;
+
+ if (inlen < 32) {
+ goto tail;
+ } else {
+ continue;
+ }
+ }
+ __m512i lo = _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in));
+ __m512i hi = _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in, 1));
+
+
+ __m512i taglo = _mm512_set1_epi32(0x8080e000);
+ __m512i taghi = taglo;
+
+ const __m512i fc00masked = _mm512_and_epi32(in, _mm512_set1_epi16(int16_t(0xfc00)));
+ const __mmask32 hisurr = _mm512_mask_cmp_epu16_mask(
+ inmask, fc00masked, _mm512_set1_epi16(int16_t(0xd800)), _MM_CMPINT_EQ);
+ const __mmask32 losurr = _mm512_cmp_epu16_mask(
+ fc00masked, _mm512_set1_epi16(int16_t(0xdc00)), _MM_CMPINT_EQ);
+
+ int carryout = 0;
+ if (!_kortestz_mask32_u8(hisurr, losurr)) {
+ // handle surrogates
+
+ __m512i los = _mm512_alignr_epi32(hi, lo, 1);
+ __m512i his = _mm512_alignr_epi32(lo, hi, 1);
+
+ const __mmask32 hisurrhi = _kshiftri_mask32(hisurr, 16);
+ taglo =
+ _mm512_mask_mov_epi32(taglo,__mmask16(hisurr), _mm512_set1_epi32(0x808080f0));
+ taghi =
+ _mm512_mask_mov_epi32(taghi, __mmask16(hisurrhi), _mm512_set1_epi32(0x808080f0));
+
+ lo = _mm512_mask_slli_epi32(lo, __mmask16(hisurr), lo, 10);
+ hi = _mm512_mask_slli_epi32(hi, __mmask16(hisurrhi), hi, 10);
+ los = _mm512_add_epi32(los, _mm512_set1_epi32(0xfca02400));
+ his = _mm512_add_epi32(his, _mm512_set1_epi32(0xfca02400));
+ lo = _mm512_mask_add_epi32(lo, __mmask16(hisurr), lo, los);
+ hi = _mm512_mask_add_epi32(hi, __mmask16(hisurrhi), hi, his);
+
+ carryout = _cvtu32_mask32(_kshiftri_mask32(hisurr, 30));
+
+ const uint32_t h = _cvtmask32_u32(hisurr);
+ const uint32_t l = _cvtmask32_u32(losurr);
+ // check for mismatched surrogates
+ if ((h + h + carry) ^ l) {
+ const uint32_t lonohi = l & ~(h + h + carry);
+ const uint32_t hinolo = h & ~(l >> 1);
+ inlen = _tzcnt_u32(hinolo | lonohi);
+ inmask = __mmask32(0x7fffffff & ((1 << inlen) - 1));
+ in = _mm512_maskz_mov_epi16(inmask, in);
+ adjust = (int)inlen - 31;
+ inlen = 0;
+ goto failiteration;
+ }
+ }
+
+ hi = _mm512_maskz_mov_epi32(_cvtu32_mask16(0x7fff),hi);
+ carry = carryout;
+
+ __m512i mslo =
+ _mm512_multishift_epi64_epi8(_mm512_set1_epi64(0x20262c3200060c12), lo);
+
+ __m512i mshi =
+ _mm512_multishift_epi64_epi8(_mm512_set1_epi64(0x20262c3200060c12), hi);
+
+ const __mmask32 outmask = __mmask32(_kandn_mask64(losurr, inmask));
+ const __mmask64 outmhi = _kshiftri_mask64(outmask, 16);
+
+ const __mmask32 is1byte = __mmask32(_knot_mask64(is234byte));
+ const __mmask64 is1bhi = _kshiftri_mask64(is1byte, 16);
+ const __mmask64 is12bhi = _kshiftri_mask64(is12byte, 16);
+
+ taglo =
+ _mm512_mask_mov_epi32(taglo, __mmask16(is12byte), _mm512_set1_epi32(0x80c00000));
+ taghi =
+ _mm512_mask_mov_epi32(taghi, __mmask16(is12bhi), _mm512_set1_epi32(0x80c00000));
+ __m512i magiclo = _mm512_mask_blend_epi32(__mmask16(outmask), _mm512_set1_epi32(0xffffffff),
+ _mm512_set1_epi32(0x00010101));
+ __m512i magichi = _mm512_mask_blend_epi32(__mmask16(outmhi), _mm512_set1_epi32(0xffffffff),
+ _mm512_set1_epi32(0x00010101));
+
+
+ magiclo = _mm512_mask_blend_epi32(__mmask16(outmask), _mm512_set1_epi32(0xffffffff),
+ _mm512_set1_epi32(0x00010101));
+ magichi = _mm512_mask_blend_epi32(__mmask16(outmhi), _mm512_set1_epi32(0xffffffff),
+ _mm512_set1_epi32(0x00010101));
+
+ mslo = _mm512_ternarylogic_epi32(mslo, _mm512_set1_epi32(0x3f3f3f3f), taglo,
+ 0xea); // A&B|C
+ mshi = _mm512_ternarylogic_epi32(mshi, _mm512_set1_epi32(0x3f3f3f3f), taghi,
+ 0xea);
+ mslo = _mm512_mask_slli_epi32(mslo, __mmask16(is1byte), lo, 24);
+
+ mshi = _mm512_mask_slli_epi32(mshi, __mmask16(is1bhi), hi, 24);
+
+ const __mmask64 wantlo = _mm512_cmp_epu8_mask(mslo, magiclo, _MM_CMPINT_NLT);
+ const __mmask64 wanthi = _mm512_cmp_epu8_mask(mshi, magichi, _MM_CMPINT_NLT);
+ const __m512i outlo = _mm512_maskz_compress_epi8(wantlo, mslo);
+ const __m512i outhi = _mm512_maskz_compress_epi8(wanthi, mshi);
+ const uint64_t wantlo_uint64 = _cvtmask64_u64(wantlo);
+ const uint64_t wanthi_uint64 = _cvtmask64_u64(wanthi);
+
+ uint64_t advlo = _mm_popcnt_u64(wantlo_uint64);
+ uint64_t advhi = _mm_popcnt_u64(wanthi_uint64);
+
+ _mm512_mask_storeu_epi8(outbuf, _cvtu64_mask64(_pext_u64(wantlo_uint64, wantlo_uint64)), outlo);
+ _mm512_mask_storeu_epi8(outbuf + advlo, _cvtu64_mask64(_pext_u64(wanthi_uint64, wanthi_uint64)), outhi);
+ outbuf += advlo + advhi;
+ }
+ outbuf -= adjust;
+
+tail:
+ if (inlen != 0) {
+ // We must have inlen < 31.
+ inmask = _cvtu32_mask32((1 << inlen) - 1);
+ in = _mm512_maskz_loadu_epi16(inmask, inbuf);
+ if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); }
+ adjust = inlen - 31;
+ inlen = 0;
+ goto lastiteration;
+ }
+ *outlen = (outbuf - outbuf_orig) + adjust;
+ return ((inbuf - inbuf_orig) + adjust);
+}
+/* end file src/icelake/icelake_convert_utf16_to_utf8.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf16_to_utf32.inl.cpp */
+// file included directly
+
+/*
+ Returns a pair: the first unprocessed byte from buf and utf32_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::tuple<const char16_t*, char32_t*, bool> convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const char16_t* end = buf + len;
+ const __m512i v_fc00 = _mm512_set1_epi16((uint16_t)0xfc00);
+ const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800);
+ const __m512i v_dc00 = _mm512_set1_epi16((uint16_t)0xdc00);
+ __mmask32 carry{0};
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ while (std::distance(buf,end) >= 32) {
+ // Always safe because buf + 32 <= end so that end - buf >= 32 bytes:
+ __m512i in = _mm512_loadu_si512((__m512i*)buf);
+ if(big_endian) { in = _mm512_shuffle_epi8(in, byteflip); }
+
+ // H - bitmask for high surrogates
+ const __mmask32 H = _mm512_cmpeq_epi16_mask(_mm512_and_si512(in, v_fc00), v_d800);
+ // H - bitmask for low surrogates
+ const __mmask32 L = _mm512_cmpeq_epi16_mask(_mm512_and_si512(in, v_fc00), v_dc00);
+
+ if ((H|L)) {
+ // surrogate pair(s) in a register
+ const __mmask32 V = (L ^ (carry | (H << 1))); // A high surrogate must be followed by low one and a low one must be preceded by a high one.
+ // If valid, V should be equal to 0
+
+ if(V == 0) {
+ // valid case
+ /*
+ Input surrogate pair:
+ |1101.11aa.aaaa.aaaa|1101.10bb.bbbb.bbbb|
+ low surrogate high surrogate
+ */
+ /* 1. Expand all code units to 32-bit code units
+ in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0000.0000.0000.1101.10bb.bbbb.bbbb|
+ */
+ const __m512i first = _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in));
+ const __m512i second = _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in,1));
+
+ /* 2. Shift by one 16-bit word to align low surrogates with high surrogates
+ in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0000.0000.0000.1101.10bb.bbbb.bbbb|
+ shifted |????.????.????.????.????.????.????.????|0000.0000.0000.0000.1101.11aa.aaaa.aaaa|
+ */
+ const __m512i shifted_first = _mm512_alignr_epi32(second, first, 1);
+ const __m512i shifted_second = _mm512_alignr_epi32(_mm512_setzero_si512(), second, 1);
+
+ /* 3. Align all high surrogates in first and second by shifting to the left by 10 bits
+ |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0011.0110.bbbb.bbbb.bb00.0000.0000|
+ */
+ const __m512i aligned_first = _mm512_mask_slli_epi32(first, (__mmask16)H, first, 10);
+ const __m512i aligned_second = _mm512_mask_slli_epi32(second, (__mmask16)(H>>16), second, 10);
+
+ /* 4. Remove surrogate prefixes and add offset 0x10000 by adding in, shifted and constant
+ in |0000.0000.0000.0000.1101.11aa.aaaa.aaaa|0000.0011.0110.bbbb.bbbb.bb00.0000.0000|
+ shifted |????.????.????.????.????.????.????.????|0000.0000.0000.0000.1101.11aa.aaaa.aaaa|
+ constant|1111.1100.1010.0000.0010.0100.0000.0000|1111.1100.1010.0000.0010.0100.0000.0000|
+ */
+ const __m512i constant = _mm512_set1_epi32((uint32_t)0xfca02400);
+ const __m512i added_first = _mm512_mask_add_epi32(aligned_first, (__mmask16)H, aligned_first, shifted_first);
+ const __m512i utf32_first = _mm512_mask_add_epi32(added_first, (__mmask16)H, added_first, constant);
+
+ const __m512i added_second = _mm512_mask_add_epi32(aligned_second, (__mmask16)(H>>16), aligned_second, shifted_second);
+ const __m512i utf32_second = _mm512_mask_add_epi32(added_second, (__mmask16)(H>>16), added_second, constant);
+
+ // 5. Store all valid UTF-32 code units (low surrogate positions and 32nd word are invalid)
+ const __mmask32 valid = ~L & 0x7fffffff;
+ // We deliberately do a _mm512_maskz_compress_epi32 followed by storeu_epi32
+ // to ease performance portability to Zen 4.
+ const __m512i compressed_first = _mm512_maskz_compress_epi32((__mmask16)(valid), utf32_first);
+ const size_t howmany1 = count_ones((uint16_t)(valid));
+ _mm512_storeu_si512((__m512i *) utf32_output, compressed_first);
+ utf32_output += howmany1;
+ const __m512i compressed_second = _mm512_maskz_compress_epi32((__mmask16)(valid >> 16), utf32_second);
+ const size_t howmany2 = count_ones((uint16_t)(valid >> 16));
+ // The following could be unsafe in some cases?
+ //_mm512_storeu_epi32((__m512i *) utf32_output, compressed_second);
+ _mm512_mask_storeu_epi32((__m512i *) utf32_output, __mmask16((1<<howmany2)-1), compressed_second);
+ utf32_output += howmany2;
+ // Only process 31 code units, but keep track if the 31st word is a high surrogate as a carry
+ buf += 31;
+ carry = (H >> 30) & 0x1;
+ } else {
+ // invalid case
+ return std::make_tuple(buf+carry, utf32_output, false);
+ }
+ } else {
+ // no surrogates
+ // extend all thirty-two 16-bit code units to thirty-two 32-bit code units
+ _mm512_storeu_si512((__m512i *)(utf32_output), _mm512_cvtepu16_epi32(_mm512_castsi512_si256(in)));
+ _mm512_storeu_si512((__m512i *)(utf32_output) + 1, _mm512_cvtepu16_epi32(_mm512_extracti32x8_epi32(in,1)));
+ utf32_output += 32;
+ buf += 32;
+ carry = 0;
+ }
+ } // while
+ return std::make_tuple(buf+carry, utf32_output, true);
+}
+/* end file src/icelake/icelake_convert_utf16_to_utf32.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf32_to_latin1.inl.cpp */
+// file included directly
+size_t icelake_convert_utf32_to_latin1(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const char32_t *end = buf + len;
+ __m512i v_0xFF = _mm512_set1_epi32(0xff);
+ __m512i shufmask = _mm512_set_epi8(
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60,
+ 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0);
+ while (buf + 16 <= end) {
+ __m512i in = _mm512_loadu_si512((__m512i *)buf);
+ if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) {
+ return 0;
+ }
+ _mm_storeu_si128((__m128i *)latin1_output,
+ _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in)));
+ latin1_output += 16;
+ buf += 16;
+ }
+ if (buf < end) {
+ uint16_t mask = uint16_t((1 << (end - buf)) - 1);
+ __m512i in = _mm512_maskz_loadu_epi32(mask, buf);
+ if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) {
+ return 0;
+ }
+ _mm_mask_storeu_epi8(
+ latin1_output, mask,
+ _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in)));
+ }
+ return len;
+}
+
+std::pair<result, char *>
+icelake_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const char32_t *end = buf + len;
+ const char32_t *start = buf;
+ __m512i v_0xFF = _mm512_set1_epi32(0xff);
+ __m512i shufmask = _mm512_set_epi8(
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60,
+ 56, 52, 48, 44, 40, 36, 32, 28, 24, 20, 16, 12, 8, 4, 0);
+ while (buf + 16 <= end) {
+ __m512i in = _mm512_loadu_si512((__m512i *)buf);
+ if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) {
+ while (uint32_t(*buf) <= 0xff) {
+ *latin1_output++ = uint8_t(*buf++);
+ }
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start),
+ latin1_output);
+ }
+ _mm_storeu_si128((__m128i *)latin1_output,
+ _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in)));
+ latin1_output += 16;
+ buf += 16;
+ }
+ if (buf < end) {
+ uint16_t mask = uint16_t((1 << (end - buf)) - 1);
+ __m512i in = _mm512_maskz_loadu_epi32(mask, buf);
+ if (_mm512_cmpgt_epu32_mask(in, v_0xFF)) {
+ while (uint32_t(*buf) <= 0xff) {
+ *latin1_output++ = uint8_t(*buf++);
+ }
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start),
+ latin1_output);
+ }
+ _mm_mask_storeu_epi8(
+ latin1_output, mask,
+ _mm512_castsi512_si128(_mm512_permutexvar_epi8(shufmask, in)));
+ }
+ return std::make_pair(result(error_code::SUCCESS, len), latin1_output);
+}
+/* end file src/icelake/icelake_convert_utf32_to_latin1.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf32_to_utf8.inl.cpp */
+// file included directly
+
+// Todo: currently, this is just the haswell code, optimize for icelake kernel.
+std::pair<const char32_t*, char*> avx512_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) {
+ const char32_t* end = buf + len;
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
+ const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
+ const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
+ const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
+ __m256i running_max = _mm256_setzero_si256();
+ __m256i forbidden_bytemask = _mm256_setzero_si256();
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1);
+ running_max = _mm256_max_epu32(_mm256_max_epu32(in, running_max), nextin);
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff));
+ in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
+
+ // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp)
+
+ if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in_16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in_16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // Must check for overflow in packing
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+ if (saturation_bitmask == 0xffffffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
+ forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800));
+
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ } else {
+ // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes.
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // may require large, non-trivial tables?
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII)
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) { // 2-byte
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000 )==0) { // 3-byte
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else { // 4-byte
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ // check for invalid input
+ const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
+ if(static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(_mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) {
+ return std::make_pair(nullptr, utf8_output);
+ }
+
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); }
+
+ return std::make_pair(buf, utf8_output);
+}
+
+// Todo: currently, this is just the haswell code, optimize for icelake kernel.
+std::pair<result, char*> avx512_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) {
+ const char32_t* end = buf + len;
+ const char32_t* start = buf;
+
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
+ const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
+ const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
+ const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
+ const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1);
+ // Check for too large input
+ const __m256i max_input = _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff);
+ if(static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output);
+ }
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff));
+ in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
+
+ // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp)
+
+ if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in_16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in_16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // Must check for overflow in packing
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+ if (saturation_bitmask == 0xffffffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+
+ // Check for illegal surrogate code units
+ const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
+ const __m256i forbidden_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output);
+ }
+
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ } else {
+ // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes.
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // may require large, non-trivial tables?
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII)
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) { // 2-byte
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000 )==0) { // 3-byte
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else { // 4-byte
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
+}
+/* end file src/icelake/icelake_convert_utf32_to_utf8.inl.cpp */
+/* begin file src/icelake/icelake_convert_utf32_to_utf16.inl.cpp */
+// file included directly
+
+// Todo: currently, this is just the haswell code, optimize for icelake kernel.
+template <endianness big_endian>
+std::pair<const char32_t*, char16_t*> avx512_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const char32_t* end = buf + len;
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+ __m256i forbidden_bytemask = _mm256_setzero_si256();
+
+
+ while (buf + 8 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
+
+ // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+
+ if (saturation_bitmask == 0xffffffff) {
+ const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
+ forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
+
+ __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ // check for invalid input
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); }
+
+ return std::make_pair(buf, utf16_output);
+}
+
+// Todo: currently, this is just the haswell code, optimize for icelake kernel.
+template <endianness big_endian>
+std::pair<result, char16_t*> avx512_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 8 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
+
+ // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+
+ if (saturation_bitmask == 0xffffffff) {
+ const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
+ const __m256i forbidden_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output);
+ }
+
+ __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
+}
+/* end file src/icelake/icelake_convert_utf32_to_utf16.inl.cpp */
+/* begin file src/icelake/icelake_ascii_validation.inl.cpp */
+// file included directly
+
+bool validate_ascii(const char* buf, size_t len) {
+ const char* end = buf + len;
+ const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80);
+ __m512i running_or = _mm512_setzero_si512();
+ for (; buf + 64 <= end; buf += 64) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)buf);
+ running_or = _mm512_ternarylogic_epi32(running_or, utf8, ascii, 0xf8); // running_or | (utf8 & ascii)
+ }
+ if(buf < end) {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8((uint64_t(1) << (end-buf)) - 1,(const __m512i*)buf);
+ running_or = _mm512_ternarylogic_epi32(running_or, utf8, ascii, 0xf8); // running_or | (utf8 & ascii)
+ }
+ return (_mm512_test_epi8_mask(running_or, running_or) == 0);
+}
+/* end file src/icelake/icelake_ascii_validation.inl.cpp */
+/* begin file src/icelake/icelake_utf32_validation.inl.cpp */
+// file included directly
+
+const char32_t* validate_utf32(const char32_t* buf, size_t len) {
+ const char32_t* end = len >= 16 ? buf + len - 16 : nullptr;
+
+ const __m512i offset = _mm512_set1_epi32((uint32_t)0xffff2000);
+ __m512i currentmax = _mm512_setzero_si512();
+ __m512i currentoffsetmax = _mm512_setzero_si512();
+
+ while (buf <= end) {
+ __m512i utf32 = _mm512_loadu_si512((const __m512i*)buf);
+ buf += 16;
+ currentoffsetmax = _mm512_max_epu32(_mm512_add_epi32(utf32, offset), currentoffsetmax);
+ currentmax = _mm512_max_epu32(utf32, currentmax);
+ }
+
+ const __m512i standardmax = _mm512_set1_epi32((uint32_t)0x10ffff);
+ const __m512i standardoffsetmax = _mm512_set1_epi32((uint32_t)0xfffff7ff);
+ __m512i is_zero = _mm512_xor_si512(_mm512_max_epu32(currentmax, standardmax), standardmax);
+ if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) {
+ return nullptr;
+ }
+ is_zero = _mm512_xor_si512(_mm512_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if (_mm512_test_epi8_mask(is_zero, is_zero) != 0) {
+ return nullptr;
+ }
+
+ return buf;
+}
+/* end file src/icelake/icelake_utf32_validation.inl.cpp */
+/* begin file src/icelake/icelake_convert_latin1_to_utf8.inl.cpp */
+// file included directly
+
+static inline size_t latin1_to_utf8_avx512_vec(__m512i input, size_t input_len, char *utf8_output, int mask_output) {
+ __mmask64 nonascii = _mm512_movepi8_mask(input);
+ size_t output_size = input_len + (size_t)count_ones(nonascii);
+
+ // Mask to denote whether the byte is a leading byte that is not ascii
+ __mmask64 sixth =
+ _mm512_cmpge_epu8_mask(input, _mm512_set1_epi8(-64)); //binary representation of -64: 1100 0000
+
+ const uint64_t alternate_bits = UINT64_C(0x5555555555555555);
+ uint64_t ascii = ~nonascii;
+ // the bits in ascii are inverted and zeros are interspersed in between them
+ uint64_t maskA = ~_pdep_u64(ascii, alternate_bits);
+ uint64_t maskB = ~_pdep_u64(ascii>>32, alternate_bits);
+
+ // interleave bytes from top and bottom halves (abcd...ABCD -> aAbBcCdD)
+ __m512i input_interleaved = _mm512_permutexvar_epi8(_mm512_set_epi32(
+ 0x3f1f3e1e, 0x3d1d3c1c, 0x3b1b3a1a, 0x39193818,
+ 0x37173616, 0x35153414, 0x33133212, 0x31113010,
+ 0x2f0f2e0e, 0x2d0d2c0c, 0x2b0b2a0a, 0x29092808,
+ 0x27072606, 0x25052404, 0x23032202, 0x21012000
+ ), input);
+
+ // double size of each byte, and insert the leading byte 1100 0010
+
+/*
+upscale the bytes to 16-bit value, adding the 0b11000000 leading byte in the process.
+We adjust for the bytes that have their two most significant bits. This takes care of the first 32 bytes, assuming we interleaved the bytes. */
+ __m512i outputA = _mm512_shldi_epi16(input_interleaved, _mm512_set1_epi8(-62), 8);
+ outputA = _mm512_mask_add_epi16(
+ outputA,
+ (__mmask32)sixth,
+ outputA,
+ _mm512_set1_epi16(1 - 0x4000)); // 1- 0x4000 = 1100 0000 0000 0001????
+
+ // in the second 32-bit half, set first or second option based on whether original input is leading byte (second case) or not (first case)
+ __m512i leadingB = _mm512_mask_blend_epi16(
+ (__mmask32)(sixth>>32),
+ _mm512_set1_epi16(0x00c2), // 0000 0000 1101 0010
+ _mm512_set1_epi16(0x40c3));// 0100 0000 1100 0011
+ __m512i outputB = _mm512_ternarylogic_epi32(
+ input_interleaved,
+ leadingB,
+ _mm512_set1_epi16((short)0xff00),
+ (240 & 170) ^ 204); // (input_interleaved & 0xff00) ^ leadingB
+
+ // prune redundant bytes
+ outputA = _mm512_maskz_compress_epi8(maskA, outputA);
+ outputB = _mm512_maskz_compress_epi8(maskB, outputB);
+
+
+ size_t output_sizeA = (size_t)count_ones((uint32_t)nonascii) + 32;
+
+ if(mask_output) {
+ if(input_len > 32) { // is the second half of the input vector used?
+ __mmask64 write_mask = _bzhi_u64(~0ULL, (unsigned int)output_sizeA);
+ _mm512_mask_storeu_epi8(utf8_output, write_mask, outputA);
+ utf8_output += output_sizeA;
+ write_mask = _bzhi_u64(~0ULL, (unsigned int)(output_size - output_sizeA));
+ _mm512_mask_storeu_epi8(utf8_output, write_mask, outputB);
+ } else {
+ __mmask64 write_mask = _bzhi_u64(~0ULL, (unsigned int)output_size);
+ _mm512_mask_storeu_epi8(utf8_output, write_mask, outputA);
+ }
+ } else {
+ _mm512_storeu_si512(utf8_output, outputA);
+ utf8_output += output_sizeA;
+ _mm512_storeu_si512(utf8_output, outputB);
+ }
+ return output_size;
+}
+
+static inline size_t latin1_to_utf8_avx512_branch(__m512i input, char *utf8_output) {
+ __mmask64 nonascii = _mm512_movepi8_mask(input);
+ if(nonascii) {
+ return latin1_to_utf8_avx512_vec(input, 64, utf8_output, 0);
+ } else {
+ _mm512_storeu_si512(utf8_output, input);
+ return 64;
+ }
+}
+
+size_t latin1_to_utf8_avx512_start(const char *buf, size_t len, char *utf8_output) {
+ char *start = utf8_output;
+ size_t pos = 0;
+ // if there's at least 128 bytes remaining, we don't need to mask the output
+ for (; pos + 128 <= len; pos += 64) {
+ __m512i input = _mm512_loadu_si512((__m512i *)(buf + pos));
+ utf8_output += latin1_to_utf8_avx512_branch(input, utf8_output);
+ }
+ // in the last 128 bytes, the first 64 may require masking the output
+ if (pos + 64 <= len) {
+ __m512i input = _mm512_loadu_si512((__m512i *)(buf + pos));
+ utf8_output += latin1_to_utf8_avx512_vec(input, 64, utf8_output, 1);
+ pos += 64;
+ }
+ // with the last 64 bytes, the input also needs to be masked
+ if (pos < len) {
+ __mmask64 load_mask = _bzhi_u64(~0ULL, (unsigned int)(len - pos));
+ __m512i input = _mm512_maskz_loadu_epi8(load_mask, (__m512i *)(buf + pos));
+ utf8_output += latin1_to_utf8_avx512_vec(input, len - pos, utf8_output, 1);
+ }
+ return (size_t)(utf8_output - start);
+}
+/* end file src/icelake/icelake_convert_latin1_to_utf8.inl.cpp */
+/* begin file src/icelake/icelake_convert_latin1_to_utf16.inl.cpp */
+// file included directly
+template <endianness big_endian>
+size_t icelake_convert_latin1_to_utf16(const char *latin1_input, size_t len,
+ char16_t *utf16_output) {
+ size_t rounded_len = len & ~0x1F; // Round down to nearest multiple of 32
+
+ __m512i byteflip = _mm512_setr_epi64(0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001, 0x0e0f0c0d0a0b0809);
+ for (size_t i = 0; i < rounded_len; i += 32) {
+ // Load 32 Latin1 characters into a 256-bit register
+ __m256i in = _mm256_loadu_si256((__m256i *)&latin1_input[i]);
+ // Zero extend each set of 8 Latin1 characters to 32 16-bit integers
+ __m512i out = _mm512_cvtepu8_epi16(in);
+ if (big_endian) {
+ out = _mm512_shuffle_epi8(out, byteflip);
+ }
+ // Store the results back to memory
+ _mm512_storeu_si512((__m512i *)&utf16_output[i], out);
+ }
+ if (rounded_len != len) {
+ uint32_t mask = uint32_t(1 << (len - rounded_len)) - 1;
+ __m256i in = _mm256_maskz_loadu_epi8(mask, latin1_input + rounded_len);
+
+ // Zero extend each set of 8 Latin1 characters to 32 16-bit integers
+ __m512i out = _mm512_cvtepu8_epi16(in);
+ if (big_endian) {
+ out = _mm512_shuffle_epi8(out, byteflip);
+ }
+ // Store the results back to memory
+ _mm512_mask_storeu_epi16(utf16_output + rounded_len, mask, out);
+ }
+
+ return len;
+}
+/* end file src/icelake/icelake_convert_latin1_to_utf16.inl.cpp */
+/* begin file src/icelake/icelake_convert_latin1_to_utf32.inl.cpp */
+std::pair<const char*, char32_t*> avx512_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
+ size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
+
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ // Load 16 Latin1 characters into a 128-bit register
+ __m128i in = _mm_loadu_si128((__m128i*)&buf[i]);
+
+ // Zero extend each set of 8 Latin1 characters to 16 32-bit integers using vpmovzxbd
+ __m512i out = _mm512_cvtepu8_epi32(in);
+
+ // Store the results back to memory
+ _mm512_storeu_si512((__m512i*)&utf32_output[i], out);
+ }
+
+ // Return pointers pointing to where we left off
+ return std::make_pair(buf + rounded_len, utf32_output + rounded_len);
+}
+/* end file src/icelake/icelake_convert_latin1_to_utf32.inl.cpp */
+/* begin file src/icelake/icelake_base64.inl.cpp */
+// file included directly
+/**
+ * References and further reading:
+ *
+ * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the
+ * speed of a memory copy, Software: Practice and Experience 50 (2), 2020.
+ * https://arxiv.org/abs/1910.05109
+ *
+ * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2
+ * Instructions, ACM Transactions on the Web 12 (3), 2018.
+ * https://arxiv.org/abs/1704.00605
+ *
+ * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings.
+ * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force,
+ * Request for Comments: 4648.
+ *
+ * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization.
+ * http://www.alfredklomp.com/programming/sse-base64/. (2014).
+ *
+ * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD
+ * acceleration. https://github.com/aklomp/base64. (2014).
+ *
+ * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014).
+ * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/
+ *
+ * Nick Kopp. 2013. Base64 Encoding on a GPU.
+ * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013).
+ */
+
+struct block64 {
+ __m512i chunks[1];
+};
+
+template <bool base64_url>
+size_t encode_base64(char *dst, const char *src, size_t srclen,
+ base64_options options) {
+ // credit: Wojciech Muła
+ const uint8_t *input = (const uint8_t *)src;
+
+ uint8_t *out = (uint8_t *)dst;
+ static const char *lookup_tbl =
+ base64_url
+ ? "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
+ : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+ const __m512i shuffle_input = _mm512_setr_epi32(
+ 0x01020001, 0x04050304, 0x07080607, 0x0a0b090a, 0x0d0e0c0d, 0x10110f10,
+ 0x13141213, 0x16171516, 0x191a1819, 0x1c1d1b1c, 0x1f201e1f, 0x22232122,
+ 0x25262425, 0x28292728, 0x2b2c2a2b, 0x2e2f2d2e);
+ const __m512i lookup =
+ _mm512_loadu_si512(reinterpret_cast<const __m512i *>(lookup_tbl));
+ const __m512i multi_shifts = _mm512_set1_epi64(UINT64_C(0x3036242a1016040a));
+ size_t i = 0;
+ for (; i + 64 <= srclen; i += 48) {
+ const __m512i v =
+ _mm512_loadu_si512(reinterpret_cast<const __m512i *>(input + i));
+ const __m512i in = _mm512_permutexvar_epi8(shuffle_input, v);
+ const __m512i indices = _mm512_multishift_epi64_epi8(multi_shifts, in);
+ const __m512i result = _mm512_permutexvar_epi8(indices, lookup);
+ _mm512_storeu_si512(reinterpret_cast<__m512i *>(out), result);
+ out += 64;
+ }
+ return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i,
+ srclen - i, options);
+}
+
+template <bool base64_url>
+static inline uint64_t to_base64_mask(block64 *b, bool *error) {
+ __m512i input = b->chunks[0];
+ const __m512i ascii_space_tbl = _mm512_set_epi8(
+ 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 13, 12, 0, 10, 9,
+ 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0,
+ 32, 0, 0, 13, 12, 0, 10, 9, 0, 0, 0, 0, 0, 0, 0, 0, 32);
+ __m512i lookup0;
+ if (base64_url) {
+ lookup0 = _mm512_set_epi8(
+ -128, -128, -128, -128, -128, -128, 61, 60, 59, 58, 57, 56, 55, 54, 53,
+ 52, -128, -128, 62, -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -1, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -1,
+ -128, -128, -1, -1, -128, -128, -128, -128, -128, -128, -128, -128, -1);
+ } else {
+ lookup0 = _mm512_set_epi8(
+ -128, -128, -128, -128, -128, -128, 61, 60, 59, 58, 57, 56, 55, 54, 53,
+ 52, 63, -128, -128, -128, 62, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -1, -128, -128, -128, -128, -128, -128, -128, -128,
+ -128, -128, -128, -128, -128, -128, -128, -128, -128, -128, -1, -128,
+ -128, -1, -1, -128, -128, -128, -128, -128, -128, -128, -128, -128);
+ }
+ __m512i lookup1;
+ if (base64_url) {
+ lookup1 = _mm512_set_epi8(
+ -128, -128, -128, -128, -128, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42,
+ 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, -128,
+ 63, -128, -128, -128, -128, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15,
+ 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -128);
+ } else {
+ lookup1 = _mm512_set_epi8(
+ -128, -128, -128, -128, -128, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42,
+ 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, -128,
+ -128, -128, -128, -128, -128, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16,
+ 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, -128);
+ }
+
+ const __m512i translated = _mm512_permutex2var_epi8(lookup0, input, lookup1);
+ const __m512i combined = _mm512_or_si512(translated, input);
+ const __mmask64 mask = _mm512_movepi8_mask(combined);
+ if (mask) {
+ const __mmask64 spaces = _mm512_cmpeq_epi8_mask(
+ _mm512_shuffle_epi8(ascii_space_tbl, input), input);
+ *error |= (mask != spaces);
+ }
+ b->chunks[0] = translated;
+
+ return mask;
+}
+
+static inline void copy_block(block64 *b, char *output) {
+ _mm512_storeu_si512(reinterpret_cast<__m512i *>(output), b->chunks[0]);
+}
+
+static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) {
+ uint64_t nmask = ~mask;
+ __m512i c = _mm512_maskz_compress_epi8(nmask, b->chunks[0]);
+ _mm512_storeu_si512(reinterpret_cast<__m512i *>(output), c);
+ return _mm_popcnt_u64(nmask);
+}
+
+// The caller of this function is responsible to ensure that there are 64 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char *src) {
+ b->chunks[0] = _mm512_loadu_si512(reinterpret_cast<const __m512i *>(src));
+}
+
+// The caller of this function is responsible to ensure that there are 128 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char16_t *src) {
+ __m512i m1 = _mm512_loadu_si512(reinterpret_cast<const __m512i *>(src));
+ __m512i m2 = _mm512_loadu_si512(reinterpret_cast<const __m512i *>(src + 32));
+ __m512i p = _mm512_packus_epi16(m1, m2);
+ b->chunks[0] =
+ _mm512_permutexvar_epi64(_mm512_setr_epi64(0, 2, 4, 6, 1, 3, 5, 7), p);
+}
+
+static inline void base64_decode(char *out, __m512i str) {
+ const __m512i merge_ab_and_bc =
+ _mm512_maddubs_epi16(str, _mm512_set1_epi32(0x01400140));
+ const __m512i merged =
+ _mm512_madd_epi16(merge_ab_and_bc, _mm512_set1_epi32(0x00011000));
+ const __m512i pack = _mm512_set_epi8(
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 60, 61, 62, 56, 57, 58,
+ 52, 53, 54, 48, 49, 50, 44, 45, 46, 40, 41, 42, 36, 37, 38, 32, 33, 34,
+ 28, 29, 30, 24, 25, 26, 20, 21, 22, 16, 17, 18, 12, 13, 14, 8, 9, 10, 4,
+ 5, 6, 0, 1, 2);
+ const __m512i shuffled = _mm512_permutexvar_epi8(pack, merged);
+ _mm512_mask_storeu_epi8(
+ (__m512i *)out, 0xffffffffffff,
+ shuffled); // mask would be 0xffffffffffff since we write 48 bytes.
+}
+// decode 64 bytes and output 48 bytes
+static inline void base64_decode_block(char *out, const char *src) {
+ base64_decode(out,
+ _mm512_loadu_si512(reinterpret_cast<const __m512i *>(src)));
+}
+static inline void base64_decode_block(char *out, block64 *b) {
+ base64_decode(out, b->chunks[0]);
+}
+
+template <bool base64_url, typename chartype>
+result compress_decode_base64(char *dst, const chartype *src, size_t srclen,
+ base64_options options) {
+ const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value
+ : tables::base64::to_base64_value;
+ size_t equalsigns = 0;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 1;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 2;
+ }
+ }
+ const chartype *const srcinit = src;
+ const char *const dstinit = dst;
+ const chartype *const srcend = src + srclen;
+
+ // figure out why block_size == 2 is sometimes best???
+ constexpr size_t block_size = 6;
+ char buffer[block_size * 64];
+ char *bufferptr = buffer;
+ if (srclen >= 64) {
+ const chartype *const srcend64 = src + srclen - 64;
+ while (src <= srcend64) {
+ block64 b;
+ load_block(&b, src);
+ src += 64;
+ bool error = false;
+ uint64_t badcharmask = to_base64_mask<base64_url>(&b, &error);
+ if (error) {
+ src -= 64;
+ while (src < srcend && to_base64[uint8_t(*src)] <= 64) {
+ src++;
+ }
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ if (badcharmask != 0) {
+ // optimization opportunity: check for simple masks like those made of
+ // continuous 1s followed by continuous 0s. And masks containing a
+ // single bad character.
+ bufferptr += compress_block(&b, badcharmask, bufferptr);
+ } else if (bufferptr != buffer) {
+ copy_block(&b, bufferptr);
+ bufferptr += 64;
+ } else {
+ base64_decode_block(dst, &b);
+ dst += 48;
+ }
+ if (bufferptr >= (block_size - 1) * 64 + buffer) {
+ for (size_t i = 0; i < (block_size - 1); i++) {
+ base64_decode_block(dst, buffer + i * 64);
+ dst += 48;
+ }
+ std::memcpy(buffer, buffer + (block_size - 1) * 64,
+ 64); // 64 might be too much
+ bufferptr -= (block_size - 1) * 64;
+ }
+ }
+ }
+
+ char *buffer_start = buffer;
+ // Optimization note: if this is almost full, then it is worth our
+ // time, otherwise, we should just decode directly.
+ int last_block = (int)((bufferptr - buffer_start) % 64);
+ if (last_block != 0 && srcend - src + last_block >= 64) {
+
+ while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ *bufferptr = char(val);
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ bufferptr += (val <= 63);
+ src++;
+ }
+ }
+
+ for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
+ base64_decode_block(dst, buffer_start);
+ dst += 48;
+ }
+ if ((bufferptr - buffer_start) % 64 != 0) {
+ while (buffer_start + 4 < bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 4);
+ dst += 3;
+ buffer_start += 4;
+ }
+ if (buffer_start + 4 <= bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+ dst += 3;
+ buffer_start += 4;
+ }
+ // we may have 1, 2 or 3 bytes left and we need to decode them so let us
+ // bring in src content
+ int leftover = int(bufferptr - buffer_start);
+ if (leftover > 0) {
+ while (leftover < 4 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ buffer_start[leftover] = char(val);
+ leftover += (val <= 63);
+ src++;
+ }
+
+ if (leftover == 1) {
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ if (leftover == 2) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ dst += 1;
+ } else if (leftover == 3) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6) +
+ (uint32_t(buffer_start[2]) << 1 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+
+ std::memcpy(dst, &triple, 2);
+ dst += 2;
+ } else {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+ dst += 3;
+ }
+ }
+ }
+ if (src < srcend + equalsigns) {
+ result r =
+ scalar::base64::base64_tail_decode(dst, src, srcend - src, options);
+ if (r.error == error_code::INVALID_BASE64_CHARACTER) {
+ r.count += size_t(src - srcinit);
+ return r;
+ } else {
+ r.count += size_t(dst - dstinit);
+ }
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ r.error = error_code::INVALID_BASE64_CHARACTER;
+ }
+ }
+ return r;
+ }
+ if(equalsigns > 0) {
+ if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)};
+ }
+ }
+ return {SUCCESS, size_t(dst - dstinit)};
+}
+/* end file src/icelake/icelake_base64.inl.cpp */
+
+
+#include <cstdint>
+
+} // namespace
+} // namespace icelake
+} // namespace simdutf
+
+namespace simdutf {
+namespace icelake {
+
+simdutf_warn_unused int
+implementation::detect_encodings(const char *input,
+ size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ if (length % 2 == 0) {
+ const char *buf = input;
+
+ const char *start = buf;
+ const char *end = input + length;
+
+ bool is_utf8 = true;
+ bool is_utf16 = true;
+ bool is_utf32 = true;
+
+ int out = 0;
+
+ avx512_utf8_checker checker{};
+ __m512i currentmax = _mm512_setzero_si512();
+ while (buf + 64 <= end) {
+ __m512i in = _mm512_loadu_si512((__m512i *)buf);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates =
+ _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if (surrogates) {
+ is_utf8 = false;
+
+ // Can still be either UTF-16LE or UTF-32 depending on the positions
+ // of the surrogates To be valid UTF-32, a surrogate cannot be in the
+ // two most significant bytes of any 32-bit word. On the other hand, to
+ // be valid UTF-16LE, at least one surrogate must be in the two most
+ // significant bytes of a 32-bit word since they always come in pairs in
+ // UTF-16LE. Note that we always proceed in multiple of 4 before this
+ // point so there is no offset in 32-bit code units.
+
+ if ((surrogates & 0xaaaaaaaa) != 0) {
+ is_utf32 = false;
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(
+ diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ return simdutf::encoding_type::unspecified;
+ }
+
+ bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
+ if (ends_with_high) {
+ buf +=
+ 31 *
+ sizeof(char16_t); // advance only by 31 code units so that we start
+ // with the high surrogate on the next round.
+ } else {
+ buf += 32 * sizeof(char16_t);
+ }
+ is_utf16 = validate_utf16le(reinterpret_cast<const char16_t *>(buf),
+ (end - buf) / sizeof(char16_t));
+ if (!is_utf16) {
+ return simdutf::encoding_type::unspecified;
+
+ } else {
+ return simdutf::encoding_type::UTF16_LE;
+ }
+
+ } else {
+ is_utf16 = false;
+ // Check for UTF-32
+ if (length % 4 == 0) {
+ const char32_t *input32 = reinterpret_cast<const char32_t *>(buf);
+ const char32_t *end32 =
+ reinterpret_cast<const char32_t *>(start) + length / 4;
+ if (validate_utf32(input32, end32 - input32)) {
+ return simdutf::encoding_type::UTF32_LE;
+ }
+ }
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+ // If no surrogate, validate under other encodings as well
+
+ // UTF-32 validation
+ currentmax = _mm512_max_epu32(in, currentmax);
+
+ // UTF-8 validation
+ checker.check_next_input(in);
+
+ buf += 64;
+ }
+
+ // Check which encodings are possible
+
+ if (is_utf8) {
+ size_t current_length = static_cast<size_t>(buf - start);
+ if (current_length != length) {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8(
+ (1ULL << (length - current_length)) - 1, (const __m512i *)buf);
+ checker.check_next_input(utf8);
+ }
+ checker.check_eof();
+ if (!checker.errors()) {
+ out |= simdutf::encoding_type::UTF8;
+ }
+ }
+
+ if (is_utf16 && scalar::utf16::validate<endianness::LITTLE>(
+ reinterpret_cast<const char16_t *>(buf),
+ (length - (buf - start)) / 2)) {
+ out |= simdutf::encoding_type::UTF16_LE;
+ }
+
+ if (is_utf32 && (length % 4 == 0)) {
+ currentmax = _mm512_max_epu32(
+ _mm512_maskz_loadu_epi8(
+ (1ULL << (length - static_cast<size_t>(buf - start))) - 1,
+ (const __m512i *)buf),
+ currentmax);
+ __mmask16 outside_range = _mm512_cmp_epu32_mask(currentmax, _mm512_set1_epi32(0x10ffff),
+ _MM_CMPINT_GT);
+ if (outside_range == 0) {
+ out |= simdutf::encoding_type::UTF32_LE;
+ }
+ }
+
+ return out;
+ } else if (implementation::validate_utf8(input, length)) {
+ return simdutf::encoding_type::UTF8;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ avx512_utf8_checker checker{};
+ const char* ptr = buf;
+ const char* end = ptr + len;
+ for (; ptr + 64 <= end; ptr += 64) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ checker.check_next_input(utf8);
+ }
+ {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - ptr))-1, (const __m512i*)ptr);
+ checker.check_next_input(utf8);
+ }
+ checker.check_eof();
+ return ! checker.errors();
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ avx512_utf8_checker checker{};
+ const char* ptr = buf;
+ const char* end = ptr + len;
+ size_t count{0};
+ for (; ptr + 64 <= end; ptr += 64) {
+ const __m512i utf8 = _mm512_loadu_si512((const __m512i*)ptr);
+ checker.check_next_input(utf8);
+ if(checker.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(buf), reinterpret_cast<const char*>(buf + count), len - count);
+ res.count += count;
+ return res;
+ }
+ count += 64;
+ }
+ {
+ const __m512i utf8 = _mm512_maskz_loadu_epi8((1ULL<<(end - ptr))-1, (const __m512i*)ptr);
+ checker.check_next_input(utf8);
+ if(checker.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(buf), reinterpret_cast<const char*>(buf + count), len - count);
+ res.count += count;
+ return res;
+ } else {
+ return result(error_code::SUCCESS, len);
+ }
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return icelake::validate_ascii(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ const char* buf_orig = buf;
+ const char* end = buf + len;
+ const __m512i ascii = _mm512_set1_epi8((uint8_t)0x80);
+ for (; buf + 64 <= end; buf += 64) {
+ const __m512i input = _mm512_loadu_si512((const __m512i*)buf);
+ __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
+ if(notascii) {
+ return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u64(notascii));
+ }
+ }
+ {
+ const __m512i input = _mm512_maskz_loadu_epi8((1ULL<<(end - buf))-1, (const __m512i*)buf);
+ __mmask64 notascii = _mm512_cmp_epu8_mask(input, ascii, _MM_CMPINT_NLT);
+ if(notascii) {
+ return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u64(notascii));
+ }
+ }
+ return result(error_code::SUCCESS, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ const char16_t *end = buf + len;
+
+ for(;buf + 32 <= end; ) {
+ __m512i in = _mm512_loadu_si512((__m512i*)buf);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ return false;
+ }
+ bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
+ if(ends_with_high) {
+ buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round.
+ } else {
+ buf += 32;
+ }
+ } else {
+ buf += 32;
+ }
+ }
+ if(buf < end) {
+ __m512i in = _mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ const char16_t *end = buf + len;
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ for(;buf + 32 <= end; ) {
+ __m512i in = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)buf), byteflip);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ return false;
+ }
+ bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
+ if(ends_with_high) {
+ buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round.
+ } else {
+ buf += 32;
+ }
+ } else {
+ buf += 32;
+ }
+ }
+ if(buf < end) {
+ __m512i in = _mm512_shuffle_epi8(_mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf), byteflip);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ const char16_t *start_buf = buf;
+ const char16_t *end = buf + len;
+ for(;buf + 32 <= end; ) {
+ __m512i in = _mm512_loadu_si512((__m512i*)buf);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1));
+ uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1));
+ return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high));
+ }
+ bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
+ if(ends_with_high) {
+ buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round.
+ } else {
+ buf += 32;
+ }
+ } else {
+ buf += 32;
+ }
+ }
+ if(buf < end) {
+ __m512i in = _mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1));
+ uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1));
+ return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high));
+ }
+ }
+ }
+ return result(error_code::SUCCESS, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ const char16_t *start_buf = buf;
+ const char16_t *end = buf + len;
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ for(;buf + 32 <= end; ) {
+ __m512i in = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)buf), byteflip);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1));
+ uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1));
+ return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high));
+ }
+ bool ends_with_high = ((highsurrogates & 0x80000000) != 0);
+ if(ends_with_high) {
+ buf += 31; // advance only by 31 code units so that we start with the high surrogate on the next round.
+ } else {
+ buf += 32;
+ }
+ } else {
+ buf += 32;
+ }
+ }
+ if(buf < end) {
+ __m512i in = _mm512_shuffle_epi8(_mm512_maskz_loadu_epi16((1<<(end-buf))-1,(__m512i*)buf), byteflip);
+ __m512i diff = _mm512_sub_epi16(in, _mm512_set1_epi16(uint16_t(0xD800)));
+ __mmask32 surrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0800)));
+ if(surrogates) {
+ __mmask32 highsurrogates = _mm512_cmplt_epu16_mask(diff, _mm512_set1_epi16(uint16_t(0x0400)));
+ __mmask32 lowsurrogates = surrogates ^ highsurrogates;
+ // high must be followed by low
+ if ((highsurrogates << 1) != lowsurrogates) {
+ uint32_t extra_low = _tzcnt_u32(lowsurrogates &~(highsurrogates << 1));
+ uint32_t extra_high = _tzcnt_u32(highsurrogates &~(lowsurrogates >> 1));
+ return result(error_code::SURROGATE, (buf - start_buf) + (extra_low < extra_high ? extra_low : extra_high));
+ }
+ }
+ }
+ return result(error_code::SUCCESS, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
+ const char32_t * tail = icelake::validate_utf32(buf, len);
+ if (tail) {
+ return scalar::utf32::validate(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+
+ const char32_t* end = len >= 16 ? buf + len - 16 : nullptr;
+ const char32_t* buf_orig = buf;
+ while (buf <= end) {
+ __m512i utf32 = _mm512_loadu_si512((const __m512i*)buf);
+ __mmask16 outside_range = _mm512_cmp_epu32_mask(utf32, _mm512_set1_epi32(0x10ffff),
+ _MM_CMPINT_GT);
+ if (outside_range) {
+ return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u32(outside_range));
+ }
+
+ __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
+
+ __mmask16 surrogate_range = _mm512_cmp_epu32_mask(utf32_off, _mm512_set1_epi32(0xfffff7ff),
+ _MM_CMPINT_GT);
+ if (surrogate_range) {
+ return result(error_code::SURROGATE, buf - buf_orig + _tzcnt_u32(surrogate_range));
+ }
+ buf += 16;
+ }
+ if(buf < buf_orig + len) {
+ __m512i utf32 = _mm512_maskz_loadu_epi32(__mmask16((1<<(buf_orig + len - buf))-1),(const __m512i*)buf);
+ __mmask16 outside_range = _mm512_cmp_epu32_mask(utf32, _mm512_set1_epi32(0x10ffff),
+ _MM_CMPINT_GT);
+ if (outside_range) {
+ return result(error_code::TOO_LARGE, buf - buf_orig + _tzcnt_u32(outside_range));
+ }
+ __m512i utf32_off = _mm512_add_epi32(utf32, _mm512_set1_epi32(0xffff2000));
+
+ __mmask16 surrogate_range = _mm512_cmp_epu32_mask(utf32_off, _mm512_set1_epi32(0xfffff7ff),
+ _MM_CMPINT_GT);
+ if (surrogate_range) {
+ return result(error_code::SURROGATE, buf - buf_orig + _tzcnt_u32(surrogate_range));
+ }
+ }
+
+ return result(error_code::SUCCESS, len);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept {
+ return icelake::latin1_to_utf8_avx512_start(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return icelake_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return icelake_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char*, char32_t*> ret = avx512_convert_latin1_to_utf32(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake::utf8_to_latin1_avx512(buf, len, latin1_output);
+}
+
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept {
+ // Initialize output length and input length counters
+ size_t inlen = 0;
+
+ // First, try to convert as much as possible using the SIMD implementation.
+ inlen = icelake::utf8_to_latin1_avx512(buf, len, latin1_output);
+
+ // If we have completely converted the string
+ if(inlen == len) {
+ return {simdutf::SUCCESS, len};
+ }
+
+ // Else if there are remaining bytes, use the scalar function to process them.
+ // Note: This is assuming scalar::utf8_to_latin1::convert_with_errors is a function that takes
+ // the input buffer, length, and output buffer, and returns a result object with an error code
+ // and the number of characters processed.
+ result res = scalar::utf8_to_latin1::convert_with_errors(buf + inlen, len - inlen, latin1_output + inlen);
+ res.count += inlen; // Add the number of characters processed by the SIMD implementation
+
+ return res;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake::valid_utf8_to_latin1_avx512(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.second == nullptr) {
+ return 0;
+ }
+ return ret.second - utf16_output;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16_result ret = fast_avx512_convert_utf8_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.second == nullptr) {
+ return 0;
+ }
+ return ret.second - utf16_output;
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return fast_avx512_convert_utf8_to_utf16_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16_result ret = icelake::valid_utf8_to_fixed_length<endianness::LITTLE, char16_t>(buf, len, utf16_output);
+ size_t saved_bytes = ret.second - utf16_output;
+ const char* end = buf + len;
+ if (ret.first == end) {
+ return saved_bytes;
+ }
+
+ // Note: AVX512 procedure looks up 4 bytes forward, and
+ // correctly converts multi-byte chars even if their
+ // continuation bytes lie outsiede 16-byte window.
+ // It meas, we have to skip continuation bytes from
+ // the beginning ret.first, as they were already consumed.
+ while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
+ ret.first += 1;
+ }
+
+ if (ret.first != end) {
+ const size_t scalar_saved_bytes = scalar::utf8_to_utf16::convert_valid<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16_result ret = icelake::valid_utf8_to_fixed_length<endianness::BIG, char16_t>(buf, len, utf16_output);
+ size_t saved_bytes = ret.second - utf16_output;
+ const char* end = buf + len;
+ if (ret.first == end) {
+ return saved_bytes;
+ }
+
+ // Note: AVX512 procedure looks up 4 bytes forward, and
+ // correctly converts multi-byte chars even if their
+ // continuation bytes lie outsiede 16-byte window.
+ // It meas, we have to skip continuation bytes from
+ // the beginning ret.first, as they were already consumed.
+ while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
+ ret.first += 1;
+ }
+
+ if (ret.first != end) {
+ const size_t scalar_saved_bytes = scalar::utf8_to_utf16::convert_valid<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+
+ return saved_bytes;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_out) const noexcept {
+ uint32_t * utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
+ utf8_to_utf32_result ret = icelake::validating_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(buf, len, utf32_output);
+ if (ret.second == nullptr)
+ return 0;
+
+ size_t saved_bytes = ret.second - utf32_output;
+ const char* end = buf + len;
+ if (ret.first == end) {
+ return saved_bytes;
+ }
+
+ // Note: the AVX512 procedure looks up 4 bytes forward, and
+ // correctly converts multi-byte chars even if their
+ // continuation bytes lie outside 16-byte window.
+ // It means, we have to skip continuation bytes from
+ // the beginning ret.first, as they were already consumed.
+ while (ret.first != end and ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
+ ret.first += 1;
+ }
+
+ if (ret.first != end) {
+ const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert(
+ ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32) const noexcept {
+ uint32_t * utf32_output = reinterpret_cast<uint32_t *>(utf32);
+ auto ret = icelake::validating_utf8_to_fixed_length_with_constant_checks<endianness::LITTLE, uint32_t>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) {
+ auto new_buf = std::get<0>(ret);
+ // rewind_and_convert_with_errors will seek a potential error from new_buf onward,
+ // with the ability to go back up to new_buf - buf bytes, and read len - (new_buf - buf) bytes forward.
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(new_buf - buf, new_buf, len - (new_buf - buf), reinterpret_cast<char32_t *>(std::get<1>(ret)));
+ res.count += (std::get<0>(ret) - buf);
+ return res;
+ }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ const char* end = buf + len;
+ if (std::get<0>(ret) == end) {
+ return {simdutf::SUCCESS, saved_bytes};
+ }
+
+ // Note: the AVX512 procedure looks up 4 bytes forward, and
+ // correctly converts multi-byte chars even if their
+ // continuation bytes lie outside 16-byte window.
+ // It means, we have to skip continuation bytes from
+ // the beginning ret.first, as they were already consumed.
+ while (std::get<0>(ret) != end and ((uint8_t(*std::get<0>(ret)) & 0xc0) == 0x80)) {
+ std::get<0>(ret) += 1;
+ }
+
+ if (std::get<0>(ret) != end) {
+ auto scalar_result = scalar::utf8_to_utf32::convert_with_errors(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), reinterpret_cast<char32_t *>(utf32_output) + saved_bytes);
+ if (scalar_result.error != simdutf::SUCCESS) {
+ scalar_result.count += (std::get<0>(ret) - buf);
+ } else {
+ scalar_result.count += saved_bytes;
+ }
+ return scalar_result;
+ }
+
+ return {simdutf::SUCCESS, size_t(std::get<1>(ret) - utf32_output)};
+}
+
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_out) const noexcept {
+ uint32_t * utf32_output = reinterpret_cast<uint32_t *>(utf32_out);
+ utf8_to_utf32_result ret = icelake::valid_utf8_to_fixed_length<endianness::LITTLE, uint32_t>(buf, len, utf32_output);
+ size_t saved_bytes = ret.second - utf32_output;
+ const char* end = buf + len;
+ if (ret.first == end) {
+ return saved_bytes;
+ }
+
+ // Note: AVX512 procedure looks up 4 bytes forward, and
+ // correctly converts multi-byte chars even if their
+ // continuation bytes lie outsiede 16-byte window.
+ // It meas, we have to skip continuation bytes from
+ // the beginning ret.first, as they were already consumed.
+ while (ret.first != end && ((uint8_t(*ret.first) & 0xc0) == 0x80)) {
+ ret.first += 1;
+ }
+
+ if (ret.first != end) {
+ const size_t scalar_saved_bytes = scalar::utf8_to_utf32::convert_valid(
+ ret.first, len - (ret.first - buf), utf32_out + saved_bytes);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+
+ return saved_bytes;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf16_to_latin1<endianness::LITTLE>(buf,len,latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf16_to_latin1<endianness::BIG>(buf,len,latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(buf,len,latin1_output).first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf,len,latin1_output).first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement custom function
+ return convert_utf16be_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement custom function
+ return convert_utf16le_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ size_t outlen;
+ size_t inlen = utf16_to_utf8_avx512i<endianness::LITTLE>(buf, len, (unsigned char*)utf8_output, &outlen);
+ if(inlen != len) { return 0; }
+ return outlen;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ size_t outlen;
+ size_t inlen = utf16_to_utf8_avx512i<endianness::BIG>(buf, len, (unsigned char*)utf8_output, &outlen);
+ if(inlen != len) { return 0; }
+ return outlen;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ size_t outlen;
+ size_t inlen = utf16_to_utf8_avx512i<endianness::LITTLE>(buf, len, (unsigned char*)utf8_output, &outlen);
+ if(inlen != len) {
+ result res = scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(buf + inlen, len - outlen, utf8_output + outlen);
+ res.count += inlen;
+ return res;
+ }
+ return {simdutf::SUCCESS, outlen};
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ size_t outlen;
+ size_t inlen = utf16_to_utf8_avx512i<endianness::BIG>(buf, len, (unsigned char*)utf8_output, &outlen);
+ if(inlen != len) {
+ result res = scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(buf + inlen, len - outlen, utf8_output + outlen);
+ res.count += inlen;
+ return res;
+ }
+ return {simdutf::SUCCESS, outlen};
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16le_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16be_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf32_to_latin1(buf,len,latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf32_to_latin1_with_errors(buf,len,latin1_output).first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return icelake_convert_utf32_to_latin1(buf,len,latin1_output);
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = avx512_convert_utf32_to_utf8(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = icelake::avx512_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf32_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = avx512_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = avx512_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = avx512_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = avx512_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16le(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16be(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) { return 0; }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::LITTLE>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) { return 0; }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::BIG>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) {
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ scalar_res.count += (std::get<0>(ret) - buf);
+ return scalar_res;
+ }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_res.error) {
+ scalar_res.count += (std::get<0>(ret) - buf);
+ return scalar_res;
+ } else {
+ scalar_res.count += saved_bytes;
+ return scalar_res;
+ }
+ }
+ return simdutf::result(simdutf::SUCCESS, saved_bytes);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) {
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ scalar_res.count += (std::get<0>(ret) - buf);
+ return scalar_res;
+ }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_res.error) {
+ scalar_res.count += (std::get<0>(ret) - buf);
+ return scalar_res;
+ } else {
+ scalar_res.count += saved_bytes;
+ return scalar_res;
+ }
+ }
+ return simdutf::result(simdutf::SUCCESS, saved_bytes);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) { return 0; }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::LITTLE>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::tuple<const char16_t*, char32_t*, bool> ret = icelake::convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (!std::get<2>(ret)) { return 0; }
+ size_t saved_bytes = std::get<1>(ret) - utf32_output;
+ if (std::get<0>(ret) != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::BIG>(
+ std::get<0>(ret), len - (std::get<0>(ret) - buf), std::get<1>(ret));
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ size_t pos = 0;
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ while (pos + 32 <= length) {
+ __m512i utf16 = _mm512_loadu_si512((const __m512i*)(input + pos));
+ utf16 = _mm512_shuffle_epi8(utf16, byteflip);
+ _mm512_storeu_si512(output + pos, utf16);
+ pos += 32;
+ }
+ if(pos < length) {
+ __mmask32 m((1<< (length - pos))-1);
+ __m512i utf16 = _mm512_maskz_loadu_epi16(m, (const __m512i*)(input + pos));
+ utf16 = _mm512_shuffle_epi8(utf16, byteflip);
+ _mm512_mask_storeu_epi16(output + pos, m, utf16);
+ }
+}
+
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ const char16_t* end = length >= 32 ? input + length - 32 : nullptr;
+ const char16_t* ptr = input;
+
+ const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
+ const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
+
+ size_t count{0};
+
+ while (ptr <= end) {
+ __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr);
+ ptr += 32;
+ uint64_t not_high_surrogate = static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) | _mm512_cmplt_epu16_mask(utf16, low));
+ count += count_ones(not_high_surrogate);
+ }
+
+ return count + scalar::utf16::count_code_points<endianness::LITTLE>(ptr, length - (ptr - input));
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ const char16_t* end = length >= 32 ? input + length - 32 : nullptr;
+ const char16_t* ptr = input;
+
+ const __m512i low = _mm512_set1_epi16((uint16_t)0xdc00);
+ const __m512i high = _mm512_set1_epi16((uint16_t)0xdfff);
+
+ size_t count{0};
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ while (ptr <= end) {
+ __m512i utf16 = _mm512_shuffle_epi8(_mm512_loadu_si512((__m512i*)ptr), byteflip);
+ ptr += 32;
+ uint64_t not_high_surrogate = static_cast<uint64_t>(_mm512_cmpgt_epu16_mask(utf16, high) | _mm512_cmplt_epu16_mask(utf16, low));
+ count += count_ones(not_high_surrogate);
+ }
+
+ return count + scalar::utf16::count_code_points<endianness::BIG>(ptr, length - (ptr - input));
+}
+
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
+ size_t answer = length / sizeof(__m512i) * sizeof(__m512i); // Number of 512-bit chunks that fits into the length.
+ size_t i = 0;
+ __m512i unrolled_popcount{0};
+
+ const __m512i continuation = _mm512_set1_epi8(char(0b10111111));
+
+ while (i + sizeof(__m512i) <= length) {
+ size_t iterations = (length - i) / sizeof(__m512i);
+
+ size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
+ for (; i + 8*sizeof(__m512i) <= max_i; i += 8*sizeof(__m512i)) {
+ __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
+ __m512i input2 = _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
+ __m512i input3 = _mm512_loadu_si512((const __m512i *)(str + i + 2*sizeof(__m512i)));
+ __m512i input4 = _mm512_loadu_si512((const __m512i *)(str + i + 3*sizeof(__m512i)));
+ __m512i input5 = _mm512_loadu_si512((const __m512i *)(str + i + 4*sizeof(__m512i)));
+ __m512i input6 = _mm512_loadu_si512((const __m512i *)(str + i + 5*sizeof(__m512i)));
+ __m512i input7 = _mm512_loadu_si512((const __m512i *)(str + i + 6*sizeof(__m512i)));
+ __m512i input8 = _mm512_loadu_si512((const __m512i *)(str + i + 7*sizeof(__m512i)));
+
+
+ __mmask64 mask1 = _mm512_cmple_epi8_mask(input1, continuation);
+ __mmask64 mask2 = _mm512_cmple_epi8_mask(input2, continuation);
+ __mmask64 mask3 = _mm512_cmple_epi8_mask(input3, continuation);
+ __mmask64 mask4 = _mm512_cmple_epi8_mask(input4, continuation);
+ __mmask64 mask5 = _mm512_cmple_epi8_mask(input5, continuation);
+ __mmask64 mask6 = _mm512_cmple_epi8_mask(input6, continuation);
+ __mmask64 mask7 = _mm512_cmple_epi8_mask(input7, continuation);
+ __mmask64 mask8 = _mm512_cmple_epi8_mask(input8, continuation);
+
+ __m512i mask_register = _mm512_set_epi64(mask8, mask7, mask6, mask5, mask4, mask3, mask2, mask1);
+
+
+ unrolled_popcount = _mm512_add_epi64(unrolled_popcount, _mm512_popcnt_epi64(mask_register));
+ }
+
+ for (; i <= max_i; i += sizeof(__m512i)) {
+ __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
+ uint64_t continuation_bitmask = static_cast<uint64_t>(_mm512_cmple_epi8_mask(more_input, continuation));
+ answer -= count_ones(continuation_bitmask);
+ }
+ }
+
+ __m256i first_half = _mm512_extracti64x4_epi64(unrolled_popcount, 0);
+ __m256i second_half = _mm512_extracti64x4_epi64(unrolled_popcount, 1);
+ answer -= (size_t)_mm256_extract_epi64(first_half, 0) +
+ (size_t)_mm256_extract_epi64(first_half, 1) +
+ (size_t)_mm256_extract_epi64(first_half, 2) +
+ (size_t)_mm256_extract_epi64(first_half, 3) +
+ (size_t)_mm256_extract_epi64(second_half, 0) +
+ (size_t)_mm256_extract_epi64(second_half, 1) +
+ (size_t)_mm256_extract_epi64(second_half, 2) +
+ (size_t)_mm256_extract_epi64(second_half, 3);
+
+ return answer + scalar::utf8::count_code_points(reinterpret_cast<const char *>(str + i), length - i);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept {
+ return count_utf8(buf,len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept {
+ return scalar::utf16::latin1_length_from_utf16(length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept {
+ return scalar::utf32::latin1_length_from_utf32(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ const char16_t* end = length >= 32 ? input + length - 32 : nullptr;
+ const char16_t* ptr = input;
+
+ const __m512i v_007f = _mm512_set1_epi16((uint16_t)0x007f);
+ const __m512i v_07ff = _mm512_set1_epi16((uint16_t)0x07ff);
+ const __m512i v_dfff = _mm512_set1_epi16((uint16_t)0xdfff);
+ const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800);
+
+ size_t count{0};
+
+ while (ptr <= end) {
+ __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr);
+ ptr += 32;
+ __mmask32 ascii_bitmask = _mm512_cmple_epu16_mask(utf16, v_007f);
+ __mmask32 two_bytes_bitmask = _mm512_mask_cmple_epu16_mask(~ascii_bitmask, utf16, v_07ff);
+ __mmask32 not_one_two_bytes = ~(ascii_bitmask | two_bytes_bitmask);
+ __mmask32 surrogates_bitmask = _mm512_mask_cmple_epu16_mask(not_one_two_bytes, utf16, v_dfff) & _mm512_mask_cmpge_epu16_mask(not_one_two_bytes, utf16, v_d800);
+
+ size_t ascii_count = count_ones(ascii_bitmask);
+ size_t two_bytes_count = count_ones(two_bytes_bitmask);
+ size_t surrogate_bytes_count = count_ones(surrogates_bitmask);
+ size_t three_bytes_count = 32 - ascii_count - two_bytes_count - surrogate_bytes_count;
+
+ count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 2*surrogate_bytes_count;
+ }
+
+ return count + scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(ptr, length - (ptr - input));
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ const char16_t* end = length >= 32 ? input + length - 32 : nullptr;
+ const char16_t* ptr = input;
+
+ const __m512i v_007f = _mm512_set1_epi16((uint16_t)0x007f);
+ const __m512i v_07ff = _mm512_set1_epi16((uint16_t)0x07ff);
+ const __m512i v_dfff = _mm512_set1_epi16((uint16_t)0xdfff);
+ const __m512i v_d800 = _mm512_set1_epi16((uint16_t)0xd800);
+
+ size_t count{0};
+ const __m512i byteflip = _mm512_setr_epi64(
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809,
+ 0x0607040502030001,
+ 0x0e0f0c0d0a0b0809
+ );
+ while (ptr <= end) {
+ __m512i utf16 = _mm512_loadu_si512((const __m512i*)ptr);
+ utf16 = _mm512_shuffle_epi8(utf16, byteflip);
+ ptr += 32;
+ __mmask32 ascii_bitmask = _mm512_cmple_epu16_mask(utf16, v_007f);
+ __mmask32 two_bytes_bitmask = _mm512_mask_cmple_epu16_mask(~ascii_bitmask, utf16, v_07ff);
+ __mmask32 not_one_two_bytes = ~(ascii_bitmask | two_bytes_bitmask);
+ __mmask32 surrogates_bitmask = _mm512_mask_cmple_epu16_mask(not_one_two_bytes, utf16, v_dfff) & _mm512_mask_cmpge_epu16_mask(not_one_two_bytes, utf16, v_d800);
+
+ size_t ascii_count = count_ones(ascii_bitmask);
+ size_t two_bytes_count = count_ones(two_bytes_bitmask);
+ size_t surrogate_bytes_count = count_ones(surrogates_bitmask);
+ size_t three_bytes_count = 32 - ascii_count - two_bytes_count - surrogate_bytes_count;
+ count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 2*surrogate_bytes_count;
+ }
+
+ return count + scalar::utf16::utf8_length_from_utf16<endianness::BIG>(ptr, length - (ptr - input));
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return implementation::count_utf16le(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return implementation::count_utf16be(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf16_length_from_latin1(length);
+}
+
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf32_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t length) const noexcept {
+ const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
+ size_t answer = length / sizeof(__m512i) * sizeof(__m512i);
+ size_t i = 0;
+ unsigned char v_0xFF = 0xff;
+ __m512i eight_64bits = _mm512_setzero_si512();
+ while (i + sizeof(__m512i) <= length) {
+ __m512i runner = _mm512_setzero_si512();
+ size_t iterations = (length - i) / sizeof(__m512i);
+ if (iterations > 255) {
+ iterations = 255;
+ }
+ size_t max_i = i + iterations * sizeof(__m512i) - sizeof(__m512i);
+ for (; i + 4*sizeof(__m512i) <= max_i; i += 4*sizeof(__m512i)) {
+ // Load four __m512i vectors
+ __m512i input1 = _mm512_loadu_si512((const __m512i *)(str + i));
+ __m512i input2 = _mm512_loadu_si512((const __m512i *)(str + i + sizeof(__m512i)));
+ __m512i input3 = _mm512_loadu_si512((const __m512i *)(str + i + 2*sizeof(__m512i)));
+ __m512i input4 = _mm512_loadu_si512((const __m512i *)(str + i + 3*sizeof(__m512i)));
+
+ // Generate four masks
+ __mmask64 mask1 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input1);
+ __mmask64 mask2 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input2);
+ __mmask64 mask3 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input3);
+ __mmask64 mask4 = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), input4);
+ // Apply the masks and subtract from the runner
+ __m512i not_ascii1 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask1, v_0xFF);
+ __m512i not_ascii2 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask2, v_0xFF);
+ __m512i not_ascii3 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask3, v_0xFF);
+ __m512i not_ascii4 = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask4, v_0xFF);
+
+ runner = _mm512_sub_epi8(runner, not_ascii1);
+ runner = _mm512_sub_epi8(runner, not_ascii2);
+ runner = _mm512_sub_epi8(runner, not_ascii3);
+ runner = _mm512_sub_epi8(runner, not_ascii4);
+ }
+
+ for (; i <= max_i; i += sizeof(__m512i)) {
+ __m512i more_input = _mm512_loadu_si512((const __m512i *)(str + i));
+
+ __mmask64 mask = _mm512_cmpgt_epi8_mask(_mm512_setzero_si512(), more_input);
+ __m512i not_ascii = _mm512_mask_set1_epi8(_mm512_setzero_si512(), mask, v_0xFF);
+ runner = _mm512_sub_epi8(runner, not_ascii);
+ }
+
+ eight_64bits = _mm512_add_epi64(eight_64bits, _mm512_sad_epu8(runner, _mm512_setzero_si512()));
+ }
+
+ __m256i first_half = _mm512_extracti64x4_epi64(eight_64bits, 0);
+ __m256i second_half = _mm512_extracti64x4_epi64(eight_64bits, 1);
+ answer += (size_t)_mm256_extract_epi64(first_half, 0) +
+ (size_t)_mm256_extract_epi64(first_half, 1) +
+ (size_t)_mm256_extract_epi64(first_half, 2) +
+ (size_t)_mm256_extract_epi64(first_half, 3) +
+ (size_t)_mm256_extract_epi64(second_half, 0) +
+ (size_t)_mm256_extract_epi64(second_half, 1) +
+ (size_t)_mm256_extract_epi64(second_half, 2) +
+ (size_t)_mm256_extract_epi64(second_half, 3);
+ return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast<const char *>(str + i), length - i);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos + 64 <= length; pos += 64) {
+ __m512i utf8 = _mm512_loadu_si512((const __m512i*)(input+pos));
+ uint64_t utf8_continuation_mask = _mm512_cmple_epi8_mask(utf8, _mm512_set1_epi8(-65+1));
+ // We count one word for anything that is not a continuation (so
+ // leading bytes).
+ count += 64 - count_ones(utf8_continuation_mask);
+ uint64_t utf8_4byte = _mm512_cmpge_epu8_mask(utf8, _mm512_set1_epi8(int8_t(240)));
+ count += count_ones(utf8_4byte);
+ }
+ return count + scalar::utf8::utf16_length_from_utf8(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const char32_t* end = length >= 16 ? input + length - 16 : nullptr;
+ const char32_t* ptr = input;
+
+ const __m512i v_0000_007f = _mm512_set1_epi32((uint32_t)0x7f);
+ const __m512i v_0000_07ff = _mm512_set1_epi32((uint32_t)0x7ff);
+ const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff);
+
+ size_t count{0};
+
+ while (ptr <= end) {
+ __m512i utf32 = _mm512_loadu_si512((const __m512i*)ptr);
+ ptr += 16;
+ __mmask16 ascii_bitmask = _mm512_cmple_epu32_mask(utf32, v_0000_007f);
+ __mmask16 two_bytes_bitmask = _mm512_mask_cmple_epu32_mask(_knot_mask16(ascii_bitmask), utf32, v_0000_07ff);
+ __mmask16 three_bytes_bitmask = _mm512_mask_cmple_epu32_mask(_knot_mask16(_mm512_kor(ascii_bitmask, two_bytes_bitmask)), utf32, v_0000_ffff);
+
+ size_t ascii_count = count_ones(ascii_bitmask);
+ size_t two_bytes_count = count_ones(two_bytes_bitmask);
+ size_t three_bytes_count = count_ones(three_bytes_bitmask);
+ size_t four_bytes_count = 16 - ascii_count - two_bytes_count - three_bytes_count;
+ count += ascii_count + 2*two_bytes_count + 3*three_bytes_count + 4*four_bytes_count;
+ }
+
+ return count + scalar::utf32::utf8_length_from_utf32(ptr, length - (ptr - input));
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const char32_t* end = length >= 16 ? input + length - 16 : nullptr;
+ const char32_t* ptr = input;
+
+ const __m512i v_0000_ffff = _mm512_set1_epi32((uint32_t)0x0000ffff);
+
+ size_t count{0};
+
+ while (ptr <= end) {
+ __m512i utf32 = _mm512_loadu_si512((const __m512i*)ptr);
+ ptr += 16;
+ __mmask16 surrogates_bitmask = _mm512_cmpgt_epu32_mask(utf32, v_0000_ffff);
+
+ count += 16 + count_ones(surrogates_bitmask);
+ }
+
+ return count + scalar::utf32::utf16_length_from_utf32(ptr, length - (ptr - input));
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return implementation::count_utf8(input, length);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ if(options & base64_url) {
+ return encode_base64<true>(output, input, length, options);
+ } else {
+ return encode_base64<false>(output, input, length, options);
+ }
+}
+
+} // namespace icelake
+} // namespace simdutf
+
+/* begin file src/simdutf/icelake/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_ICELAKE
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif // end of workaround
+/* end file src/simdutf/icelake/end.h */
+/* end file src/icelake/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_HASWELL
+/* begin file src/haswell/implementation.cpp */
+
+/* begin file src/simdutf/haswell/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "haswell"
+// #define SIMDUTF_IMPLEMENTATION haswell
+
+#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL
+// nothing needed.
+#else
+SIMDUTF_TARGET_HASWELL
+#endif
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_DISABLE_GCC_WARNING(-Wmaybe-uninitialized)
+#endif // end of workaround
+/* end file src/simdutf/haswell/begin.h */
+namespace simdutf {
+namespace haswell {
+namespace {
+#ifndef SIMDUTF_HASWELL_H
+#error "haswell.h must be included"
+#endif
+using namespace simd;
+
+
+simdutf_really_inline bool is_ascii(const simd8x64<uint8_t>& input) {
+ return input.reduce_or().is_ascii();
+}
+
+simdutf_unused simdutf_really_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0
+ // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
+ return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
+}
+
+simdutf_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be > 0x80
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be > 0x80
+ return simd8<bool>(is_third_byte | is_fourth_byte);
+}
+
+/* begin file src/haswell/avx2_detect_encodings.cpp */
+template<class checker>
+// len is known to be a multiple of 2 when this is called
+int avx2_detect_encodings(const char * buf, size_t len) {
+ const char* start = buf;
+ const char* end = buf + len;
+
+ bool is_utf8 = true;
+ bool is_utf16 = true;
+ bool is_utf32 = true;
+
+ int out = 0;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+
+ __m256i currentmax = _mm256_setzero_si256();
+
+ checker check{};
+
+ while(buf + 64 <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1);
+
+ const auto u0 = simd16<uint16_t>(in);
+ const auto u1 = simd16<uint16_t>(nextin);
+
+ const auto v0 = u0.shr<8>();
+ const auto v1 = u1.shr<8>();
+
+ const auto in16 = simd16<uint16_t>::pack(v0, v1);
+
+ const auto surrogates_wordmask0 = (in16 & v_f8) == v_d8;
+ uint32_t surrogates_bitmask0 = surrogates_wordmask0.to_bitmask();
+
+ // Check for surrogates
+ if (surrogates_bitmask0 != 0x0) {
+ // Cannot be UTF8
+ is_utf8 = false;
+ // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates
+ // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word.
+ // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant
+ // bytes of a 32-bit word since they always come in pairs in UTF-16LE.
+ // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units.
+
+ if ((surrogates_bitmask0 & 0xaaaaaaaa) != 0) {
+ is_utf32 = false;
+ // Code from avx2_validate_utf16le.cpp
+ const char16_t * input = reinterpret_cast<const char16_t*>(buf);
+ const char16_t* end16 = reinterpret_cast<const char16_t*>(start) + len/2;
+
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ const uint32_t V0 = ~surrogates_bitmask0;
+
+ const auto vH0 = (in16 & v_fc) == v_dc;
+ const uint32_t H0 = vH0.to_bitmask();
+
+ const uint32_t L0 = ~H0 & surrogates_bitmask0;
+
+ const uint32_t a0 = L0 & (H0 >> 1);
+ const uint32_t b0 = a0 << 1;
+ const uint32_t c0 = V0 | a0 | b0;
+
+ if (c0 == 0xffffffff) {
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else if (c0 == 0x7fffffff) {
+ input += simd16<uint16_t>::ELEMENTS * 2 - 1;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+
+ while (input + simd16<uint16_t>::ELEMENTS * 2 < end16) {
+ const auto in0 = simd16<uint16_t>(input);
+ const auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::ELEMENTS);
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in_16 = simd16<uint16_t>::pack(t0, t1);
+
+ const auto surrogates_wordmask = (in_16 & v_f8) == v_d8;
+ const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask();
+ if (surrogates_bitmask == 0x0) {
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else {
+ const uint32_t V = ~surrogates_bitmask;
+
+ const auto vH = (in_16 & v_fc) == v_dc;
+ const uint32_t H = vH.to_bitmask();
+
+ const uint32_t L = ~H & surrogates_bitmask;
+
+ const uint32_t a = L & (H >> 1);
+
+ const uint32_t b = a << 1;
+
+ const uint32_t c = V | a | b;
+
+ if (c == 0xffffffff) {
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else if (c == 0x7fffffff) {
+ input += simd16<uint16_t>::ELEMENTS * 2 - 1;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+ }
+ } else {
+ is_utf16 = false;
+ // Check for UTF-32
+ if (len % 4 == 0) {
+ const char32_t * input = reinterpret_cast<const char32_t*>(buf);
+ const char32_t* end32 = reinterpret_cast<const char32_t*>(start) + len/4;
+
+ // Must start checking for surrogates
+ __m256i currentoffsetmax = _mm256_setzero_si256();
+ const __m256i offset = _mm256_set1_epi32(0xffff2000);
+ const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff);
+
+ currentmax = _mm256_max_epu32(in, currentmax);
+ currentmax = _mm256_max_epu32(nextin, currentmax);
+
+ currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax);
+ currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(nextin, offset), currentoffsetmax);
+
+ while (input + 8 < end32) {
+ const __m256i in32 = _mm256_loadu_si256((__m256i *)input);
+ currentmax = _mm256_max_epu32(in32,currentmax);
+ currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in32, offset), currentoffsetmax);
+ input += 8;
+ }
+
+ __m256i forbidden_words = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm256_testz_si256(forbidden_words, forbidden_words) == 0) {
+ return simdutf::encoding_type::unspecified;
+ }
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+ break;
+ }
+ // If no surrogate, validate under other encodings as well
+
+ // UTF-32 validation
+ currentmax = _mm256_max_epu32(in, currentmax);
+ currentmax = _mm256_max_epu32(nextin, currentmax);
+
+ // UTF-8 validation
+ // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h
+ simd::simd8x64<uint8_t> in8(in, nextin);
+ check.check_next_input(in8);
+
+ buf += 64;
+ }
+
+ // Check which encodings are possible
+
+ if (is_utf8) {
+ if (static_cast<size_t>(buf - start) != len) {
+ uint8_t block[64]{};
+ std::memset(block, 0x20, 64);
+ std::memcpy(block, buf, len - (buf - start));
+ simd::simd8x64<uint8_t> in(block);
+ check.check_next_input(in);
+ }
+ if (!check.errors()) {
+ out |= simdutf::encoding_type::UTF8;
+ }
+ }
+
+ if (is_utf16 && scalar::utf16::validate<endianness::LITTLE>(reinterpret_cast<const char16_t*>(buf), (len - (buf - start))/2)) {
+ out |= simdutf::encoding_type::UTF16_LE;
+ }
+
+ if (is_utf32 && (len % 4 == 0)) {
+ const __m256i standardmax = _mm256_set1_epi32(0x10ffff);
+ __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax);
+ if (_mm256_testz_si256(is_zero, is_zero) == 1 && scalar::utf32::validate(reinterpret_cast<const char32_t*>(buf), (len - (buf - start))/4)) {
+ out |= simdutf::encoding_type::UTF32_LE;
+ }
+ }
+
+ return out;
+}
+/* end file src/haswell/avx2_detect_encodings.cpp */
+
+/* begin file src/haswell/avx2_validate_utf16.cpp */
+/*
+ In UTF-16 code units in range 0xD800 to 0xDFFF have special meaning.
+
+ In a vectorized algorithm we want to examine the most significant
+ nibble in order to select a fast path. If none of highest nibbles
+ are 0xD (13), than we are sure that UTF-16 chunk in a vector
+ register is valid.
+
+ Let us analyze what we need to check if the nibble is 0xD. The
+ value of the preceding nibble determines what we have:
+
+ 0xd000 .. 0xd7ff - a valid word
+ 0xd800 .. 0xdbff - low surrogate
+ 0xdc00 .. 0xdfff - high surrogate
+
+ Other constraints we have to consider:
+ - there must not be two consecutive low surrogates (0xd800 .. 0xdbff)
+ - there must not be two consecutive high surrogates (0xdc00 .. 0xdfff)
+ - there must not be sole low surrogate nor high surrogate
+
+ We're going to build three bitmasks based on the 3rd nibble:
+ - V = valid word,
+ - L = low surrogate (0xd800 .. 0xdbff)
+ - H = high surrogate (0xdc00 .. 0xdfff)
+
+ 0 1 2 3 4 5 6 7 <--- word index
+ [ V | L | H | L | H | V | V | L ]
+ 1 0 0 0 0 1 1 0 - V = valid masks
+ 0 1 0 1 0 0 0 1 - L = low surrogate
+ 0 0 1 0 1 0 0 0 - H high surrogate
+
+
+ 1 0 0 0 0 1 1 0 V = valid masks
+ 0 1 0 1 0 0 0 0 a = L & (H >> 1)
+ 0 0 1 0 1 0 0 0 b = a << 1
+ 1 1 1 1 1 1 1 0 c = V | a | b
+ ^
+ the last bit can be zero, we just consume 7 code units
+ and recheck this word in the next iteration
+*/
+
+/* Returns:
+ - pointer to the last unprocessed character (a scalar fallback should check the rest);
+ - nullptr if an error was detected.
+*/
+template <endianness big_endian>
+const char16_t* avx2_validate_utf16(const char16_t* input, size_t size) {
+ const char16_t* end = input + size;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ while (input + simd16<uint16_t>::ELEMENTS * 2 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::ELEMENTS);
+
+ if (big_endian) {
+ in0 = in0.swap_bytes();
+ in1 = in1.swap_bytes();
+ }
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in = simd16<uint16_t>::pack(t0, t1);
+
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const auto surrogates_wordmask = (in & v_f8) == v_d8;
+ const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask();
+ if (surrogates_bitmask == 0x0) {
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint32_t V = ~surrogates_bitmask;
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = (in & v_fc) == v_dc;
+ const uint32_t H = vH.to_bitmask();
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint32_t L = ~H & surrogates_bitmask;
+
+ const uint32_t a = L & (H >> 1); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint32_t b = a << 1; // Just mark that the opposite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint32_t c = V | a | b; // Combine all the masks into the final one.
+
+ if (c == 0xffffffff) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else if (c == 0x7fffffff) {
+ // The 31 lower code units of the input register contains valid UTF-16.
+ // The 31 word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += simd16<uint16_t>::ELEMENTS * 2 - 1;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+
+ return input;
+}
+
+
+template <endianness big_endian>
+const result avx2_validate_utf16_with_errors(const char16_t* input, size_t size) {
+ const char16_t* start = input;
+ const char16_t* end = input + size;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ while (input + simd16<uint16_t>::ELEMENTS * 2 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::ELEMENTS);
+
+ if (big_endian) {
+ in0 = in0.swap_bytes();
+ in1 = in1.swap_bytes();
+ }
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in = simd16<uint16_t>::pack(t0, t1);
+
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const auto surrogates_wordmask = (in & v_f8) == v_d8;
+ const uint32_t surrogates_bitmask = surrogates_wordmask.to_bitmask();
+ if (surrogates_bitmask == 0x0) {
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint32_t V = ~surrogates_bitmask;
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = (in & v_fc) == v_dc;
+ const uint32_t H = vH.to_bitmask();
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint32_t L = ~H & surrogates_bitmask;
+
+ const uint32_t a = L & (H >> 1); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint32_t b = a << 1; // Just mark that the opposite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint32_t c = V | a | b; // Combine all the masks into the final one.
+
+ if (c == 0xffffffff) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += simd16<uint16_t>::ELEMENTS * 2;
+ } else if (c == 0x7fffffff) {
+ // The 31 lower code units of the input register contains valid UTF-16.
+ // The 31 word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += simd16<uint16_t>::ELEMENTS * 2 - 1;
+ } else {
+ return result(error_code::SURROGATE, input - start);
+ }
+ }
+ }
+
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/haswell/avx2_validate_utf16.cpp */
+/* begin file src/haswell/avx2_validate_utf32le.cpp */
+/* Returns:
+ - pointer to the last unprocessed character (a scalar fallback should check the rest);
+ - nullptr if an error was detected.
+*/
+const char32_t* avx2_validate_utf32le(const char32_t* input, size_t size) {
+ const char32_t* end = input + size;
+
+ const __m256i standardmax = _mm256_set1_epi32(0x10ffff);
+ const __m256i offset = _mm256_set1_epi32(0xffff2000);
+ const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff);
+ __m256i currentmax = _mm256_setzero_si256();
+ __m256i currentoffsetmax = _mm256_setzero_si256();
+
+ while (input + 8 < end) {
+ const __m256i in = _mm256_loadu_si256((__m256i *)input);
+ currentmax = _mm256_max_epu32(in,currentmax);
+ currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax);
+ input += 8;
+ }
+ __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax);
+ if(_mm256_testz_si256(is_zero, is_zero) == 0) {
+ return nullptr;
+ }
+
+ is_zero = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm256_testz_si256(is_zero, is_zero) == 0) {
+ return nullptr;
+ }
+
+ return input;
+}
+
+
+const result avx2_validate_utf32le_with_errors(const char32_t* input, size_t size) {
+ const char32_t* start = input;
+ const char32_t* end = input + size;
+
+ const __m256i standardmax = _mm256_set1_epi32(0x10ffff);
+ const __m256i offset = _mm256_set1_epi32(0xffff2000);
+ const __m256i standardoffsetmax = _mm256_set1_epi32(0xfffff7ff);
+ __m256i currentmax = _mm256_setzero_si256();
+ __m256i currentoffsetmax = _mm256_setzero_si256();
+
+ while (input + 8 < end) {
+ const __m256i in = _mm256_loadu_si256((__m256i *)input);
+ currentmax = _mm256_max_epu32(in,currentmax);
+ currentoffsetmax = _mm256_max_epu32(_mm256_add_epi32(in, offset), currentoffsetmax);
+
+ __m256i is_zero = _mm256_xor_si256(_mm256_max_epu32(currentmax, standardmax), standardmax);
+ if(_mm256_testz_si256(is_zero, is_zero) == 0) {
+ return result(error_code::TOO_LARGE, input - start);
+ }
+
+ is_zero = _mm256_xor_si256(_mm256_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm256_testz_si256(is_zero, is_zero) == 0) {
+ return result(error_code::SURROGATE, input - start);
+ }
+ input += 8;
+ }
+
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/haswell/avx2_validate_utf32le.cpp */
+
+/* begin file src/haswell/avx2_convert_latin1_to_utf8.cpp */
+std::pair<const char *, char *> avx2_convert_latin1_to_utf8(const char *latin1_input, size_t len,
+ char *utf8_output) {
+ const char *end = latin1_input + len;
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
+ const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
+ const size_t safety_margin = 12;
+
+ while (latin1_input + 16 + safety_margin <= end) {
+ __m128i in8 = _mm_loadu_si128((__m128i *)latin1_input);
+ // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
+ const __m128i v_80 = _mm_set1_epi8((char)0x80);
+ if (_mm_testz_si128(in8, v_80)) { // ASCII fast path!!!!
+ // 1. store (16 bytes)
+ _mm_storeu_si128((__m128i *)utf8_output, in8);
+ // 2. adjust pointers
+ latin1_input += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // We proceed only with the first 16 bytes.
+ const __m256i in = _mm256_cvtepu8_epi16((in8));
+
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0000|aabb|bbbb] x 8
+ // expected output : [1100|00aa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [0000|00aa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in, 2);
+ // t1 = [0000|00aa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [1100|00aa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t *row =
+ &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t *row_2 =
+ &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2 >> 16)]
+ [0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i *)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i *)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(
+ utf8_unpacked, _mm256_setr_m128i(shuffle, shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i *)utf8_output,
+ _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i *)utf8_output,
+ _mm256_extractf128_si256(utf8_packed, 1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ latin1_input += 16;
+ continue;
+
+ } // while
+ return std::make_pair(latin1_input, utf8_output);
+}
+/* end file src/haswell/avx2_convert_latin1_to_utf8.cpp */
+/* begin file src/haswell/avx2_convert_latin1_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char*, char16_t*> avx2_convert_latin1_to_utf16(const char* latin1_input, size_t len, char16_t* utf16_output) {
+ size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 32
+
+ size_t i = 0;
+ for (; i < rounded_len; i += 16) {
+ // Load 16 bytes from the address (input + i) into a xmm register
+ __m128i xmm0 = _mm_loadu_si128(reinterpret_cast<const __m128i*>(latin1_input + i));
+
+ // Zero extend each byte in xmm0 to word and put it in another xmm register
+ __m128i xmm1 = _mm_cvtepu8_epi16(xmm0);
+
+ // Shift xmm0 to the right by 8 bytes
+ xmm0 = _mm_srli_si128(xmm0, 8);
+
+ // Zero extend each byte in the shifted xmm0 to word in xmm0
+ xmm0 = _mm_cvtepu8_epi16(xmm0);
+
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ xmm0 = _mm_shuffle_epi8(xmm0, swap);
+ xmm1 = _mm_shuffle_epi8(xmm1, swap);
+ }
+
+ // Store the contents of xmm1 into the address pointed by (output + i)
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(utf16_output + i), xmm1);
+
+ // Store the contents of xmm0 into the address pointed by (output + i + 8)
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(utf16_output + i + 8), xmm0);
+ }
+
+ return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
+
+}
+/* end file src/haswell/avx2_convert_latin1_to_utf16.cpp */
+/* begin file src/haswell/avx2_convert_latin1_to_utf32.cpp */
+std::pair<const char*, char32_t*> avx2_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
+ size_t rounded_len = ((len | 7) ^ 7); // Round down to nearest multiple of 8
+
+ for (size_t i = 0; i < rounded_len; i += 8) {
+ // Load 8 Latin1 characters into a 64-bit register
+ __m128i in = _mm_loadl_epi64((__m128i*)&buf[i]);
+
+ // Zero extend each set of 8 Latin1 characters to 8 32-bit integers using vpmovzxbd
+ __m256i out = _mm256_cvtepu8_epi32(in);
+
+ // Store the results back to memory
+ _mm256_storeu_si256((__m256i*)&utf32_output[i], out);
+ }
+
+ // return pointers pointing to where we left off
+ return std::make_pair(buf + rounded_len, utf32_output + rounded_len);
+}
+
+/* end file src/haswell/avx2_convert_latin1_to_utf32.cpp */
+
+/* begin file src/haswell/avx2_convert_utf8_to_utf16.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+
+// Convert up to 12 bytes from utf8 to utf16 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+template <endianness big_endian>
+size_t convert_masked_utf8_to_utf16(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char16_t *&utf16_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ // We first try a few fast paths.
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ __m256i ascii = _mm256_cvtepu8_epi16(in);
+ if (big_endian) {
+ const __m256i swap256 = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ ascii = _mm256_shuffle_epi8(ascii, swap256);
+ }
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf16_output), ascii);
+ utf16_output += 16; // We wrote 16 16-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
+ // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte UTF-16 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ if (big_endian) composed = _mm_shuffle_epi8(composed, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed);
+ utf16_output += 8; // We wrote 16 bytes, 8 code points.
+ return 16;
+ }
+ if(input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ __m128i composed_repacked = _mm_packus_epi32(composed, composed);
+ if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
+ utf16_output += 4;
+ return 12;
+ }
+
+ const uint8_t idx =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ simdutf::tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ if (big_endian) composed = _mm_shuffle_epi8(composed, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed);
+ utf16_output += 6; // We wrote 12 bytes, 6 code points. There is a potential overflow of 4 bytes.
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ __m128i composed_repacked = _mm_packus_epi32(composed, composed);
+ if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
+ utf16_output += 4; // Here we overflow by 8 bytes.
+ } else if (idx < 209) {
+ // TWO (2) input code-code units
+ //////////////
+ // There might be garbage inputs where a leading byte mascarades as a four-byte
+ // leading byte (by being followed by 3 continuation byte), but is not greater than
+ // 0xf0. This could trigger a buffer overflow if we only counted leading
+ // bytes of the form 0xf0 as generating surrogate pairs, without further UTF-8 validation.
+ // Thus we must be careful to ensure that only leading bytes at least as large as 0xf0 generate surrogate pairs.
+ // We do as at the cost of an extra mask.
+ /////////////
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)simdutf::tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
+ const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
+ // correct for spurious high bit
+ const __m128i correct =
+ _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
+ middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
+ const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
+ // We deliberately carry the leading four bits in highbyte if they are present,
+ // we remove them later when computing hightenbits.
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
+ // When we need to generate a surrogate pair (leading byte > 0xF0), then
+ // the corresponding 32-bit value in 'composed' will be greater than
+ // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
+ // location of the surrogate pairs.
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
+ _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
+ const __m128i composedminus =
+ _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
+ const __m128i lowtenbits =
+ _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
+ // Notice the 0x3ff mask:
+ const __m128i hightenbits = _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
+ const __m128i lowtenbitsadd =
+ _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
+ const __m128i hightenbitsadd =
+ _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
+ const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
+ __m128i surrogates =
+ _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
+ uint32_t basic_buffer[4];
+ uint32_t basic_buffer_swap[4];
+ if (big_endian) {
+ _mm_storeu_si128((__m128i *)basic_buffer_swap, _mm_shuffle_epi8(composed, swap));
+ surrogates = _mm_shuffle_epi8(surrogates, swap);
+ }
+ _mm_storeu_si128((__m128i *)basic_buffer, composed);
+ uint32_t surrogate_buffer[4];
+ _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
+ for (size_t i = 0; i < 3; i++) {
+ if(basic_buffer[i] > 0x3c00000) {
+ utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
+ utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
+ utf16_output += 2;
+ } else {
+ utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i]) : uint16_t(basic_buffer[i]);
+ utf16_output++;
+ }
+ }
+ } else {
+ // here we know that there is an error but we do not handle errors
+ }
+ return consumed;
+}
+/* end file src/haswell/avx2_convert_utf8_to_utf16.cpp */
+/* begin file src/haswell/avx2_convert_utf8_to_utf32.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+
+// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+size_t convert_masked_utf8_to_utf32(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char32_t *&utf32_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ // We first try a few fast paths.
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu8_epi32(in));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output+8), _mm256_cvtepu8_epi32(_mm_srli_si128(in,8)));
+ utf32_output += 16; // We wrote 16 32-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
+ // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte UTF-32 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ _mm256_storeu_si256((__m256i *)utf32_output, _mm256_cvtepu16_epi32(composed));
+ utf32_output += 8; // We wrote 16 bytes, 8 code points.
+ return 16;
+ }
+ if(input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 4;
+ return 12;
+ }
+ /// We do not have a fast path available, so we fallback.
+
+ const uint8_t idx =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ _mm256_storeu_si256((__m256i *)utf32_output, _mm256_cvtepu16_epi32(composed));
+ utf32_output += 6; // We wrote 24 bytes, 6 code points. There is a potential
+ // overflow of 32 - 24 = 8 bytes.
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 4;
+ } else if (idx < 209) {
+ // TWO (2) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
+ const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
+ // correct for spurious high bit
+ const __m128i correct =
+ _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
+ middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
+ const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
+ _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 3; // We wrote 3 * 4 bytes, there is a potential overflow of 4 bytes.
+ } else {
+ // here we know that there is an error but we do not handle errors
+ }
+ return consumed;
+}
+/* end file src/haswell/avx2_convert_utf8_to_utf32.cpp */
+
+/* begin file src/haswell/avx2_convert_utf16_to_latin1.cpp */
+template <endianness big_endian>
+std::pair<const char16_t *, char *>
+avx2_convert_utf16_to_latin1(const char16_t *buf, size_t len,
+ char *latin1_output) {
+ const char16_t *end = buf + len;
+ while (buf + 16 <= end) {
+ // Load 16 UTF-16 characters into 256-bit AVX2 register
+ __m256i in = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
+
+ if (!match_system(big_endian)) {
+ const __m256i swap = _mm256_setr_epi8(
+ 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
+ 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+
+ __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
+ if (_mm256_testz_si256(in, high_byte_mask)) {
+ // Pack 16-bit characters into 8-bit and store in latin1_output
+ __m128i lo = _mm256_extractf128_si256(in, 0);
+ __m128i hi = _mm256_extractf128_si256(in, 1);
+ __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo);
+ __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi);
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
+ latin1_packed_lo);
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8),
+ latin1_packed_hi);
+ // Adjust pointers for next iteration
+ buf += 16;
+ latin1_output += 16;
+ } else {
+ return std::make_pair(nullptr, reinterpret_cast<char *>(latin1_output));
+ }
+ } // while
+ return std::make_pair(buf, latin1_output);
+}
+
+template <endianness big_endian>
+std::pair<result, char *>
+avx2_convert_utf16_to_latin1_with_errors(const char16_t *buf, size_t len,
+ char *latin1_output) {
+ const char16_t *start = buf;
+ const char16_t *end = buf + len;
+ while (buf + 16 <= end) {
+ __m256i in = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(buf));
+
+ if (!big_endian) {
+ const __m256i swap = _mm256_setr_epi8(
+ 1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14, 17, 16, 19, 18,
+ 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+
+ __m256i high_byte_mask = _mm256_set1_epi16((int16_t)0xFF00);
+ if (_mm256_testz_si256(in, high_byte_mask)) {
+ __m128i lo = _mm256_extractf128_si256(in, 0);
+ __m128i hi = _mm256_extractf128_si256(in, 1);
+ __m128i latin1_packed_lo = _mm_packus_epi16(lo, lo);
+ __m128i latin1_packed_hi = _mm_packus_epi16(hi, hi);
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output),
+ latin1_packed_lo);
+ _mm_storel_epi64(reinterpret_cast<__m128i *>(latin1_output + 8),
+ latin1_packed_hi);
+ buf += 16;
+ latin1_output += 16;
+ } else {
+ // Fallback to scalar code for handling errors
+ for (int k = 0; k < 16; k++) {
+ uint16_t word = !match_system(big_endian)
+ ? scalar::utf16::swap_bytes(buf[k])
+ : buf[k];
+ if (word <= 0xff) {
+ *latin1_output++ = char(word);
+ } else {
+ return std::make_pair(
+ result{error_code::TOO_LARGE, (size_t)(buf - start + k)},
+ latin1_output);
+ }
+ }
+ buf += 16;
+ }
+ } // while
+ return std::make_pair(result{error_code::SUCCESS, (size_t)(buf - start)},
+ latin1_output);
+}
+/* end file src/haswell/avx2_convert_utf16_to_latin1.cpp */
+/* begin file src/haswell/avx2_convert_utf16_to_utf8.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ is in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+
+
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char*> avx2_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_output) {
+ const char16_t* end = buf + len;
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
+ const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ if (big_endian) {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+ // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
+ const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
+ if(_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint32_t surrogates_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x00000000) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf8_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(buf, utf8_output);
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char*> avx2_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
+ const __m256i v_c080 = _mm256_set1_epi16((int16_t)0xc080);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ if (big_endian) {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+ // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
+ const __m256i v_ff80 = _mm256_set1_epi16((int16_t)0xff80);
+ if(_mm256_testz_si256(in, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint32_t surrogates_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x00000000) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf8_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
+}
+/* end file src/haswell/avx2_convert_utf16_to_utf8.cpp */
+/* begin file src/haswell/avx2_convert_utf16_to_utf32.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+
+
+/*
+ Returns a pair: the first unprocessed byte from buf and utf32_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char32_t*> avx2_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const char16_t* end = buf + len;
+ const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
+
+ while (buf + 16 <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ if (big_endian) {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint32_t surrogates_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x00000000) {
+ // case: we extend all sixteen 16-bit code units to sixteen 32-bit code units
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8), _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in,1)));
+ utf32_output += 16;
+ buf += 16;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ // No surrogate pair
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf32_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(buf, utf32_output);
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char32_t*> avx2_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+ const __m256i v_f800 = _mm256_set1_epi16((int16_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi16((int16_t)0xd800);
+
+ while (buf + 16 <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ if (big_endian) {
+ const __m256i swap = _mm256_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14,
+ 17, 16, 19, 18, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28, 31, 30);
+ in = _mm256_shuffle_epi8(in, swap);
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m256i surrogates_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint32_t surrogates_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x00000000) {
+ // case: we extend all sixteen 16-bit code units to sixteen 32-bit code units
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output), _mm256_cvtepu16_epi32(_mm256_castsi256_si128(in)));
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(utf32_output + 8), _mm256_cvtepu16_epi32(_mm256_extractf128_si256(in,1)));
+ utf32_output += 16;
+ buf += 16;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ // No surrogate pair
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf32_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
+}
+/* end file src/haswell/avx2_convert_utf16_to_utf32.cpp */
+
+/* begin file src/haswell/avx2_convert_utf32_to_latin1.cpp */
+std::pair<const char32_t *, char *>
+avx2_convert_utf32_to_latin1(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const size_t rounded_len =
+ len & ~0x1F; // Round down to nearest multiple of 32
+
+ __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
+
+ __m256i shufmask = _mm256_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 12, 8, 4, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
+
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ __m256i in1 = _mm256_loadu_si256((__m256i *)buf);
+ __m256i in2 = _mm256_loadu_si256((__m256i *)(buf + 8));
+
+ __m256i check_combined = _mm256_or_si256(in1, in2);
+
+ if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
+ return std::make_pair(nullptr, latin1_output);
+ }
+
+ //Turn UTF32 bytes into latin 1 bytes
+ __m256i shuffled1 = _mm256_shuffle_epi8(in1, shufmask);
+ __m256i shuffled2 = _mm256_shuffle_epi8(in2, shufmask);
+
+ //move Latin1 bytes to their correct spot
+ __m256i idx1 = _mm256_set_epi32(-1, -1,-1,-1,-1,-1,4,0);
+ __m256i idx2 = _mm256_set_epi32(-1, -1,-1,-1,4,0,-1,-1);
+ __m256i reshuffled1 = _mm256_permutevar8x32_epi32(shuffled1, idx1);
+ __m256i reshuffled2 = _mm256_permutevar8x32_epi32(shuffled2, idx2);
+
+ __m256i result = _mm256_or_si256(reshuffled1, reshuffled2);
+ _mm_storeu_si128((__m128i *)latin1_output,
+ _mm256_castsi256_si128(result));
+
+ latin1_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(buf, latin1_output);
+}
+std::pair<result, char *>
+avx2_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const size_t rounded_len =
+ len & ~0x1F; // Round down to nearest multiple of 32
+
+ __m256i high_bytes_mask = _mm256_set1_epi32(0xFFFFFF00);
+ __m256i shufmask = _mm256_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
+ -1, 12, 8, 4, 0, -1, -1, -1, -1, -1, -1,
+ -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
+
+ const char32_t *start = buf;
+
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ __m256i in1 = _mm256_loadu_si256((__m256i *)buf);
+ __m256i in2 = _mm256_loadu_si256((__m256i *)(buf + 8));
+
+ __m256i check_combined = _mm256_or_si256(in1, in2);
+
+ if (!_mm256_testz_si256(check_combined, high_bytes_mask)) {
+ // Fallback to scalar code for handling errors
+ for (int k = 0; k < 8; k++) {
+ char32_t codepoint = buf[k];
+ if (codepoint <= 0xFF) {
+ *latin1_output++ = static_cast<char>(codepoint);
+ } else {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
+ latin1_output);
+ }
+ }
+ buf += 8;
+ } else {
+ __m256i shuffled1 = _mm256_shuffle_epi8(in1, shufmask);
+ __m256i shuffled2 = _mm256_shuffle_epi8(in2, shufmask);
+
+ __m256i idx1 = _mm256_set_epi32(-1, -1, -1, -1, -1, -1, 4, 0);
+ __m256i idx2 = _mm256_set_epi32(-1, -1, -1, -1, 4, 0, -1, -1);
+ __m256i reshuffled1 = _mm256_permutevar8x32_epi32(shuffled1, idx1);
+ __m256i reshuffled2 = _mm256_permutevar8x32_epi32(shuffled2, idx2);
+
+ __m256i result = _mm256_or_si256(reshuffled1, reshuffled2);
+ _mm_storeu_si128((__m128i *)latin1_output, _mm256_castsi256_si128(result));
+
+ latin1_output += 16;
+ buf += 16;
+ }
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output);
+}
+/* end file src/haswell/avx2_convert_utf32_to_latin1.cpp */
+/* begin file src/haswell/avx2_convert_utf32_to_utf8.cpp */
+std::pair<const char32_t*, char*> avx2_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) {
+ const char32_t* end = buf + len;
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
+ const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
+ const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
+ const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
+ __m256i running_max = _mm256_setzero_si256();
+ __m256i forbidden_bytemask = _mm256_setzero_si256();
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1);
+ running_max = _mm256_max_epu32(_mm256_max_epu32(in, running_max), nextin);
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff));
+ in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
+
+ // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp)
+
+ if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in_16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in_16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // Must check for overflow in packing
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+ if (saturation_bitmask == 0xffffffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
+ forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800));
+
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ } else {
+ // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes.
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // may require large, non-trivial tables?
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII)
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) { // 2-byte
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000 )==0) { // 3-byte
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else { // 4-byte
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ // check for invalid input
+ const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
+ if(static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(_mm256_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffffffff) {
+ return std::make_pair(nullptr, utf8_output);
+ }
+
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); }
+
+ return std::make_pair(buf, utf8_output);
+}
+
+
+std::pair<result, char*> avx2_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) {
+ const char32_t* end = buf + len;
+ const char32_t* start = buf;
+
+ const __m256i v_0000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ const __m256i v_ff80 = _mm256_set1_epi16((uint16_t)0xff80);
+ const __m256i v_f800 = _mm256_set1_epi16((uint16_t)0xf800);
+ const __m256i v_c080 = _mm256_set1_epi16((uint16_t)0xc080);
+ const __m256i v_7fffffff = _mm256_set1_epi32((uint32_t)0x7fffffff);
+ const __m256i v_10ffff = _mm256_set1_epi32((uint32_t)0x10ffff);
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+ __m256i nextin = _mm256_loadu_si256((__m256i*)buf+1);
+ // Check for too large input
+ const __m256i max_input = _mm256_max_epu32(_mm256_max_epu32(in, nextin), v_10ffff);
+ if(static_cast<uint32_t>(_mm256_movemask_epi8(_mm256_cmpeq_epi32(max_input, v_10ffff))) != 0xffffffff) {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output);
+ }
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m256i in_16 = _mm256_packus_epi32(_mm256_and_si256(in, v_7fffffff), _mm256_and_si256(nextin, v_7fffffff));
+ in_16 = _mm256_permute4x64_epi64(in_16, 0b11011000);
+
+ // Try to apply UTF-16 => UTF-8 routine on 256 bits (haswell/avx2_convert_utf16_to_utf8.cpp)
+
+ if(_mm256_testz_si256(in_16, v_ff80)) { // ASCII fast path!!!!
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(_mm256_castsi256_si128(in_16),_mm256_extractf128_si256(in_16,1));
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ // no bits set above 7th bit
+ const __m256i one_byte_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_ff80), v_0000);
+ const uint32_t one_byte_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m256i one_or_two_bytes_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_0000);
+ const uint32_t one_or_two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(one_or_two_bytes_bytemask));
+ if (one_or_two_bytes_bitmask == 0xffffffff) {
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m256i v_1f00 = _mm256_set1_epi16((int16_t)0x1f00);
+ const __m256i v_003f = _mm256_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m256i t0 = _mm256_slli_epi16(in_16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m256i t1 = _mm256_and_si256(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m256i t2 = _mm256_and_si256(in_16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m256i t3 = _mm256_or_si256(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m256i t4 = _mm256_or_si256(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m256i utf8_unpacked = _mm256_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ const uint32_t M0 = one_byte_bitmask & 0x55555555;
+ const uint32_t M1 = M0 >> 7;
+ const uint32_t M2 = (M1 | M0) & 0x00ff00ff;
+ // 4. pack the bytes
+
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2)][0];
+ const uint8_t* row_2 = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[uint8_t(M2>>16)][0];
+
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i shuffle_2 = _mm_loadu_si128((__m128i*)(row_2 + 1));
+
+ const __m256i utf8_packed = _mm256_shuffle_epi8(utf8_unpacked, _mm256_setr_m128i(shuffle,shuffle_2));
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_packed));
+ utf8_output += row[0];
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_packed,1));
+ utf8_output += row_2[0];
+
+ // 6. adjust pointers
+ buf += 16;
+ continue;
+ }
+ // Must check for overflow in packing
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(_mm256_or_si256(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+ if (saturation_bitmask == 0xffffffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+
+ // Check for illegal surrogate code units
+ const __m256i v_d800 = _mm256_set1_epi16((uint16_t)0xd800);
+ const __m256i forbidden_bytemask = _mm256_cmpeq_epi16(_mm256_and_si256(in_16, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output);
+ }
+
+ const __m256i dup_even = _mm256_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e,
+ 0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm256_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m256i t0 = _mm256_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m256i t1 = _mm256_and_si256(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m256i t2 = _mm256_or_si256 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m256i s0 = _mm256_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m256i s1 = _mm256_and_si256(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m256i s2 = _mm256_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m256i s3 = _mm256_or_si256(s2, simdutf_vec(0b1100000011100000));
+ const __m256i m0 = _mm256_andnot_si256(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m256i s4 = _mm256_xor_si256(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m256i out0 = _mm256_unpacklo_epi16(t2, s4);
+ const __m256i out1 = _mm256_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint32_t mask = (one_byte_bitmask & 0x55555555) |
+ (one_or_two_bytes_bitmask & 0xaaaaaaaa);
+ // Due to the wider registers, the following path is less likely to be useful.
+ /*if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m256i shuffle = _mm256_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1, 2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m256i utf8_0 = _mm256_shuffle_epi8(out0, shuffle);
+ const __m256i utf8_1 = _mm256_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_0));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_castsi256_si128(utf8_1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_0,1));
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, _mm256_extractf128_si256(utf8_1,1));
+ utf8_output += 12;
+ buf += 16;
+ continue;
+ }*/
+ const uint8_t mask0 = uint8_t(mask);
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(_mm256_castsi256_si128(out0), shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(_mm256_castsi256_si128(out1), shuffle1);
+
+ const uint8_t mask2 = static_cast<uint8_t>(mask >> 16);
+ const uint8_t* row2 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask2][0];
+ const __m128i shuffle2 = _mm_loadu_si128((__m128i*)(row2 + 1));
+ const __m128i utf8_2 = _mm_shuffle_epi8(_mm256_extractf128_si256(out0,1), shuffle2);
+
+
+ const uint8_t mask3 = static_cast<uint8_t>(mask >> 24);
+ const uint8_t* row3 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask3][0];
+ const __m128i shuffle3 = _mm_loadu_si128((__m128i*)(row3 + 1));
+ const __m128i utf8_3 = _mm_shuffle_epi8(_mm256_extractf128_si256(out1,1), shuffle3);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_2);
+ utf8_output += row2[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_3);
+ utf8_output += row3[0];
+ buf += 16;
+ } else {
+ // case: at least one 32-bit word is larger than 0xFFFF <=> it will produce four UTF-8 bytes.
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // may require large, non-trivial tables?
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) { // 1-byte (ASCII)
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) { // 2-byte
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word & 0xFFFF0000 )==0) { // 3-byte
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else { // 4-byte
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
+}
+/* end file src/haswell/avx2_convert_utf32_to_utf8.cpp */
+/* begin file src/haswell/avx2_convert_utf32_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char32_t*, char16_t*> avx2_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const char32_t* end = buf + len;
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+ __m256i forbidden_bytemask = _mm256_setzero_si256();
+
+
+ while (buf + 8 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
+
+ // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+
+ if (saturation_bitmask == 0xffffffff) {
+ const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
+ forbidden_bytemask = _mm256_or_si256(forbidden_bytemask, _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800));
+
+ __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ // check for invalid input
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); }
+
+ return std::make_pair(buf, utf16_output);
+}
+
+
+template <endianness big_endian>
+std::pair<result, char16_t*> avx2_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 8 + safety_margin <= end) {
+ __m256i in = _mm256_loadu_si256((__m256i*)buf);
+
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((int32_t)0xffff0000);
+
+ // no bits set above 16th bit <=> can pack to UTF16 without surrogate pairs
+ const __m256i saturation_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(saturation_bytemask));
+
+ if (saturation_bitmask == 0xffffffff) {
+ const __m256i v_f800 = _mm256_set1_epi32((uint32_t)0xf800);
+ const __m256i v_d800 = _mm256_set1_epi32((uint32_t)0xd800);
+ const __m256i forbidden_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm256_movemask_epi8(forbidden_bytemask)) != 0x0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output);
+ }
+
+ __m128i utf16_packed = _mm_packus_epi32(_mm256_castsi256_si128(in),_mm256_extractf128_si256(in,1));
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
+}
+/* end file src/haswell/avx2_convert_utf32_to_utf16.cpp */
+
+/* begin file src/haswell/avx2_convert_utf8_to_latin1.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+// Convert up to 12 bytes from utf8 to latin1 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+size_t convert_masked_utf8_to_latin1(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char *&latin1_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const __m128i in_second_half = _mm_loadu_si128((__m128i *)(input + 16));
+
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff; //we're only processing 12 bytes in case it`s not all ASCII
+
+ if((input_utf8_end_of_code_point_mask & 0xffffffff) == 0xffffffff) {
+ // Load the next 128 bits.
+
+ // Combine the two 128-bit registers into a single 256-bit register.
+ __m256i in_combined = _mm256_set_m128i(in_second_half, in);
+
+ // We process the data in chunks of 32 bytes.
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(latin1_output), in_combined);
+
+ latin1_output += 32; // We wrote 32 characters.
+ return 32; // We consumed 32 bytes.
+ }
+
+
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
+ latin1_output += 16; // We wrote 16 characters.
+ return 16; // We consumed 16 bytes.
+ }
+ /// We do not have a fast path available, so we fallback.
+ const uint8_t idx =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ // this indicates an invalid input:
+ if(idx >= 64) { return consumed; }
+ // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere.
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ const __m128i latin1_packed = _mm_packus_epi16(composed,composed);
+ // writing 8 bytes even though we only care about the first 6 bytes.
+ // performance note: it would be faster to use _mm_storeu_si128, we should investigate.
+ _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
+ latin1_output += 6; // We wrote 6 bytes.
+ return consumed;
+}
+/* end file src/haswell/avx2_convert_utf8_to_latin1.cpp */
+
+/* begin file src/haswell/avx2_base64.cpp */
+/**
+ * References and further reading:
+ *
+ * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the
+ * speed of a memory copy, Software: Practice and Experience 50 (2), 2020.
+ * https://arxiv.org/abs/1910.05109
+ *
+ * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2
+ * Instructions, ACM Transactions on the Web 12 (3), 2018.
+ * https://arxiv.org/abs/1704.00605
+ *
+ * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings.
+ * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force,
+ * Request for Comments: 4648.
+ *
+ * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization.
+ * http://www.alfredklomp.com/programming/sse-base64/. (2014).
+ *
+ * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD
+ * acceleration. https://github.com/aklomp/base64. (2014).
+ *
+ * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014).
+ * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/
+ *
+ * Nick Kopp. 2013. Base64 Encoding on a GPU.
+ * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013).
+ */
+
+template <bool base64_url>
+simdutf_really_inline __m256i lookup_pshufb_improved(const __m256i input) {
+ // credit: Wojciech Muła
+ __m256i result = _mm256_subs_epu8(input, _mm256_set1_epi8(51));
+ const __m256i less = _mm256_cmpgt_epi8(_mm256_set1_epi8(26), input);
+ result =
+ _mm256_or_si256(result, _mm256_and_si256(less, _mm256_set1_epi8(13)));
+ __m256i shift_LUT;
+ if (base64_url) {
+ shift_LUT = _mm256_setr_epi8(
+ 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0,
+
+ 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0);
+ } else {
+ shift_LUT = _mm256_setr_epi8(
+ 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0,
+
+ 'a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0);
+ }
+
+ result = _mm256_shuffle_epi8(shift_LUT, result);
+ return _mm256_add_epi8(result, input);
+}
+
+template <base64_options options>
+size_t encode_base64(char *dst, const char *src, size_t srclen) {
+ // credit: Wojciech Muła
+ const uint8_t *input = (const uint8_t *)src;
+
+ uint8_t *out = (uint8_t *)dst;
+ const __m256i shuf =
+ _mm256_set_epi8(10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1,
+
+ 10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1);
+ size_t i = 0;
+ for (; i + 100 <= srclen; i += 96) {
+ const __m128i lo0 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 0));
+ const __m128i hi0 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 1));
+ const __m128i lo1 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 2));
+ const __m128i hi1 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 3));
+ const __m128i lo2 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 4));
+ const __m128i hi2 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 5));
+ const __m128i lo3 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 6));
+ const __m128i hi3 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 7));
+
+ __m256i in0 = _mm256_shuffle_epi8(_mm256_set_m128i(hi0, lo0), shuf);
+ __m256i in1 = _mm256_shuffle_epi8(_mm256_set_m128i(hi1, lo1), shuf);
+ __m256i in2 = _mm256_shuffle_epi8(_mm256_set_m128i(hi2, lo2), shuf);
+ __m256i in3 = _mm256_shuffle_epi8(_mm256_set_m128i(hi3, lo3), shuf);
+
+ const __m256i t0_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x0fc0fc00));
+ const __m256i t0_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x0fc0fc00));
+ const __m256i t0_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x0fc0fc00));
+ const __m256i t0_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x0fc0fc00));
+
+ const __m256i t1_0 =
+ _mm256_mulhi_epu16(t0_0, _mm256_set1_epi32(0x04000040));
+ const __m256i t1_1 =
+ _mm256_mulhi_epu16(t0_1, _mm256_set1_epi32(0x04000040));
+ const __m256i t1_2 =
+ _mm256_mulhi_epu16(t0_2, _mm256_set1_epi32(0x04000040));
+ const __m256i t1_3 =
+ _mm256_mulhi_epu16(t0_3, _mm256_set1_epi32(0x04000040));
+
+ const __m256i t2_0 = _mm256_and_si256(in0, _mm256_set1_epi32(0x003f03f0));
+ const __m256i t2_1 = _mm256_and_si256(in1, _mm256_set1_epi32(0x003f03f0));
+ const __m256i t2_2 = _mm256_and_si256(in2, _mm256_set1_epi32(0x003f03f0));
+ const __m256i t2_3 = _mm256_and_si256(in3, _mm256_set1_epi32(0x003f03f0));
+
+ const __m256i t3_0 =
+ _mm256_mullo_epi16(t2_0, _mm256_set1_epi32(0x01000010));
+ const __m256i t3_1 =
+ _mm256_mullo_epi16(t2_1, _mm256_set1_epi32(0x01000010));
+ const __m256i t3_2 =
+ _mm256_mullo_epi16(t2_2, _mm256_set1_epi32(0x01000010));
+ const __m256i t3_3 =
+ _mm256_mullo_epi16(t2_3, _mm256_set1_epi32(0x01000010));
+
+ const __m256i input0 = _mm256_or_si256(t1_0, t3_0);
+ const __m256i input1 = _mm256_or_si256(t1_1, t3_1);
+ const __m256i input2 = _mm256_or_si256(t1_2, t3_2);
+ const __m256i input3 = _mm256_or_si256(t1_3, t3_3);
+
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(out),
+ lookup_pshufb_improved<options == base64_url>(input0));
+ out += 32;
+
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(out),
+ lookup_pshufb_improved<options == base64_url>(input1));
+ out += 32;
+
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(out),
+ lookup_pshufb_improved<options == base64_url>(input2));
+ out += 32;
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(out),
+ lookup_pshufb_improved<options == base64_url>(input3));
+ out += 32;
+ }
+ for (; i + 28 <= srclen; i += 24) {
+ // lo = [xxxx|DDDC|CCBB|BAAA]
+ // hi = [xxxx|HHHG|GGFF|FEEE]
+ const __m128i lo =
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(input + i));
+ const __m128i hi =
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(input + i + 4 * 3));
+
+ // bytes from groups A, B and C are needed in separate 32-bit lanes
+ // in = [0HHH|0GGG|0FFF|0EEE[0DDD|0CCC|0BBB|0AAA]
+ __m256i in = _mm256_shuffle_epi8(_mm256_set_m128i(hi, lo), shuf);
+
+ // this part is well commented in encode.sse.cpp
+
+ const __m256i t0 = _mm256_and_si256(in, _mm256_set1_epi32(0x0fc0fc00));
+ const __m256i t1 = _mm256_mulhi_epu16(t0, _mm256_set1_epi32(0x04000040));
+ const __m256i t2 = _mm256_and_si256(in, _mm256_set1_epi32(0x003f03f0));
+ const __m256i t3 = _mm256_mullo_epi16(t2, _mm256_set1_epi32(0x01000010));
+ const __m256i indices = _mm256_or_si256(t1, t3);
+
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(out),
+ lookup_pshufb_improved<options == base64_url>(indices));
+ out += 32;
+ }
+ return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i,
+ srclen - i, options);
+}
+
+static inline void compress(__m128i data, uint16_t mask, char *output) {
+ if (mask == 0) {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output), data);
+ return;
+ }
+ // this particular implementation was inspired by work done by @animetosho
+ // we do it in two steps, first 8 bytes and then second 8 bytes
+ uint8_t mask1 = uint8_t(mask); // least significant 8 bits
+ uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
+ // next line just loads the 64-bit values thintable_epi8[mask1] and
+ // thintable_epi8[mask2] into a 128-bit register, using only
+ // two instructions on most compilers.
+
+ __m128i shufmask = _mm_set_epi64x(tables::base64::thintable_epi8[mask2],
+ tables::base64::thintable_epi8[mask1]);
+ // we increment by 0x08 the second half of the mask
+ shufmask =
+ _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
+ // this is the version "nearly pruned"
+ __m128i pruned = _mm_shuffle_epi8(data, shufmask);
+ // we still need to put the two halves together.
+ // we compute the popcount of the first half:
+ int pop1 = tables::base64::BitsSetTable256mul2[mask1];
+ // then load the corresponding mask, what it does is to write
+ // only the first pop1 bytes from the first 8 bytes, and then
+ // it fills in with the bytes from the second 8 bytes + some filling
+ // at the end.
+ __m128i compactmask = _mm_loadu_si128(reinterpret_cast<const __m128i *>(
+ tables::base64::pshufb_combine_table + pop1 * 8));
+ __m128i answer = _mm_shuffle_epi8(pruned, compactmask);
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
+}
+
+static inline void compress(__m256i data, uint32_t mask, char *output) {
+ if (mask == 0) {
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), data);
+ return;
+ }
+ compress(_mm256_castsi256_si128(data), uint16_t(mask), output);
+ compress(_mm256_extracti128_si256(data, 1), uint16_t(mask >> 16),
+ output + _mm_popcnt_u32(~mask & 0xFFFF));
+}
+
+struct block64 {
+ __m256i chunks[2];
+};
+
+template <bool base64_url>
+static inline uint32_t to_base64_mask(__m256i *src, bool *error) {
+ const __m256i ascii_space_tbl =
+ _mm256_setr_epi8(0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xa,
+ 0x0, 0xc, 0xd, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0x9, 0xa, 0x0, 0xc, 0xd, 0x0, 0x0);
+ // credit: aqrit
+ __m256i delta_asso;
+ if (base64_url) {
+ delta_asso =
+ _mm256_setr_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0, 0x0,
+ 0x0, 0x0, 0xF, 0x0, 0xF, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0xF, 0x0, 0xF);
+ } else {
+ delta_asso = _mm256_setr_epi8(
+ 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x0F, 0x00, 0x0F, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F);
+ }
+
+ __m256i delta_values;
+ if (base64_url) {
+ delta_values = _mm256_setr_epi8(
+ 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF), uint8_t(0xBF), uint8_t(0xB9),
+ uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3), uint8_t(0xBF), uint8_t(0xE0),
+ uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF),
+ uint8_t(0xBF), uint8_t(0xB9), uint8_t(0xB9), 0x0, 0x11, uint8_t(0xC3),
+ uint8_t(0xBF), uint8_t(0xE0), uint8_t(0xB9), uint8_t(0xB9));
+ } else {
+ delta_values = _mm256_setr_epi8(
+ int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13), int8_t(0x04),
+ int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9), int8_t(0x00),
+ int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9),
+ int8_t(0xB9), int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13),
+ int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9),
+ int8_t(0x00), int8_t(0x10), int8_t(0xC3), int8_t(0xBF), int8_t(0xBF),
+ int8_t(0xB9), int8_t(0xB9));
+ }
+ __m256i check_asso;
+
+ if (base64_url) {
+ check_asso =
+ _mm256_setr_epi8(0xD, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x3,
+ 0x7, 0xB, 0x6, 0xB, 0x12, 0xD, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x1, 0x1, 0x1, 0x1, 0x3, 0x7, 0xB, 0x6, 0xB, 0x12);
+ } else {
+
+ check_asso = _mm256_setr_epi8(
+ 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x03, 0x07,
+ 0x0B, 0x0B, 0x0B, 0x0F, 0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F);
+ }
+ __m256i check_values;
+ if (base64_url) {
+ check_values = _mm256_setr_epi8(
+ 0x0, uint8_t(0x80), uint8_t(0x80), uint8_t(0x80), uint8_t(0xCF),
+ uint8_t(0xBF), uint8_t(0xD3), uint8_t(0xA6), uint8_t(0xB5),
+ uint8_t(0x86), uint8_t(0xD0), uint8_t(0x80), uint8_t(0xB0),
+ uint8_t(0x80), 0x0, 0x0, 0x0, uint8_t(0x80), uint8_t(0x80),
+ uint8_t(0x80), uint8_t(0xCF), uint8_t(0xBF), uint8_t(0xD3),
+ uint8_t(0xA6), uint8_t(0xB5), uint8_t(0x86), uint8_t(0xD0),
+ uint8_t(0x80), uint8_t(0xB0), uint8_t(0x80), 0x0, 0x0);
+ } else {
+ check_values = _mm256_setr_epi8(
+ int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0xCF),
+ int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5), int8_t(0x86),
+ int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80), int8_t(0x91),
+ int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80),
+ int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6), int8_t(0xB5),
+ int8_t(0x86), int8_t(0xD1), int8_t(0x80), int8_t(0xB1), int8_t(0x80),
+ int8_t(0x91), int8_t(0x80));
+ }
+ const __m256i shifted = _mm256_srli_epi32(*src, 3);
+ const __m256i delta_hash =
+ _mm256_avg_epu8(_mm256_shuffle_epi8(delta_asso, *src), shifted);
+ const __m256i check_hash =
+ _mm256_avg_epu8(_mm256_shuffle_epi8(check_asso, *src), shifted);
+ const __m256i out =
+ _mm256_adds_epi8(_mm256_shuffle_epi8(delta_values, delta_hash), *src);
+ const __m256i chk =
+ _mm256_adds_epi8(_mm256_shuffle_epi8(check_values, check_hash), *src);
+ const int mask = _mm256_movemask_epi8(chk);
+ if (mask) {
+ __m256i ascii_space =
+ _mm256_cmpeq_epi8(_mm256_shuffle_epi8(ascii_space_tbl, *src), *src);
+ *error |= (mask != _mm256_movemask_epi8(ascii_space));
+ }
+ *src = out;
+ return (uint32_t)mask;
+}
+
+template <bool base64_url>
+static inline uint64_t to_base64_mask(block64 *b, bool *error) {
+ *error = 0;
+ uint64_t m0 = to_base64_mask<base64_url>(&b->chunks[0], error);
+ uint64_t m1 = to_base64_mask<base64_url>(&b->chunks[1], error);
+ return m0 | (m1 << 32);
+}
+
+static inline void copy_block(block64 *b, char *output) {
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(output), b->chunks[0]);
+ _mm256_storeu_si256(reinterpret_cast<__m256i *>(output + 32), b->chunks[1]);
+}
+
+static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) {
+ uint64_t nmask = ~mask;
+ compress(b->chunks[0], uint32_t(mask), output);
+ compress(b->chunks[1], uint32_t(mask >> 32),
+ output + _mm_popcnt_u64(nmask & 0xFFFFFFFF));
+ return _mm_popcnt_u64(nmask);
+}
+
+// The caller of this function is responsible to ensure that there are 64 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char *src) {
+ b->chunks[0] = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
+ b->chunks[1] =
+ _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32));
+}
+
+// The caller of this function is responsible to ensure that there are 128 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char16_t *src) {
+ __m256i m1 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src));
+ __m256i m2 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 16));
+ __m256i m3 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32));
+ __m256i m4 = _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 48));
+ __m256i m1p = _mm256_permute2x128_si256(m1, m2, 0x20);
+ __m256i m2p = _mm256_permute2x128_si256(m1, m2, 0x31);
+ __m256i m3p = _mm256_permute2x128_si256(m3, m4, 0x20);
+ __m256i m4p = _mm256_permute2x128_si256(m3, m4, 0x31);
+ b->chunks[0] = _mm256_packus_epi16(m1p, m2p);
+ b->chunks[1] = _mm256_packus_epi16(m3p, m4p);
+}
+
+static inline void base64_decode(char *out, __m256i str) {
+ // credit: aqrit
+ const __m256i pack_shuffle =
+ _mm256_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1,
+ 2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1);
+ const __m256i t0 = _mm256_maddubs_epi16(str, _mm256_set1_epi32(0x01400140));
+ const __m256i t1 = _mm256_madd_epi16(t0, _mm256_set1_epi32(0x00011000));
+ const __m256i t2 = _mm256_shuffle_epi8(t1, pack_shuffle);
+
+ // Store the output:
+ _mm_storeu_si128((__m128i *)out, _mm256_castsi256_si128(t2));
+ _mm_storeu_si128((__m128i *)(out + 12), _mm256_extracti128_si256(t2, 1));
+}
+// decode 64 bytes and output 48 bytes
+static inline void base64_decode_block(char *out, const char *src) {
+ base64_decode(out,
+ _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src)));
+ base64_decode(out + 24, _mm256_loadu_si256(
+ reinterpret_cast<const __m256i *>(src + 32)));
+}
+static inline void base64_decode_block_safe(char *out, const char *src) {
+ base64_decode(out,
+ _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src)));
+ char buffer[32]; // We enforce safety with a buffer.
+ base64_decode(
+ buffer, _mm256_loadu_si256(reinterpret_cast<const __m256i *>(src + 32)));
+ std::memcpy(out + 24, buffer, 24);
+}
+static inline void base64_decode_block(char *out, block64 *b) {
+ base64_decode(out, b->chunks[0]);
+ base64_decode(out + 24, b->chunks[1]);
+}
+static inline void base64_decode_block_safe(char *out, block64 *b) {
+ base64_decode(out, b->chunks[0]);
+ char buffer[32]; // We enforce safety with a buffer.
+ base64_decode(buffer, b->chunks[1]);
+ std::memcpy(out + 24, buffer, 24);
+}
+
+template <bool base64_url, typename chartype>
+result compress_decode_base64(char *dst, const chartype *src, size_t srclen,
+ base64_options options) {
+ const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value
+ : tables::base64::to_base64_value;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ size_t equalsigns = 0;
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 1;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 2;
+ }
+ }
+ char *end_of_safe_64byte_zone =
+ (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst;
+
+ const chartype *const srcinit = src;
+ const char *const dstinit = dst;
+ const chartype *const srcend = src + srclen;
+
+ constexpr size_t block_size = 6;
+ static_assert(block_size >= 2, "block_size must be at least two");
+ char buffer[block_size * 64];
+ char *bufferptr = buffer;
+ if (srclen >= 64) {
+ const chartype *const srcend64 = src + srclen - 64;
+ while (src <= srcend64) {
+ block64 b;
+ load_block(&b, src);
+ src += 64;
+ bool error = false;
+ uint64_t badcharmask = to_base64_mask<base64_url>(&b, &error);
+ if (error) {
+ src -= 64;
+ while (src < srcend && to_base64[uint8_t(*src)] <= 64) {
+ src++;
+ }
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ if (badcharmask != 0) {
+ // optimization opportunity: check for simple masks like those made of
+ // continuous 1s followed by continuous 0s. And masks containing a
+ // single bad character.
+ bufferptr += compress_block(&b, badcharmask, bufferptr);
+ } else if (bufferptr != buffer) {
+ copy_block(&b, bufferptr);
+ bufferptr += 64;
+ } else {
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, &b);
+ } else {
+ base64_decode_block(dst, &b);
+ }
+ dst += 48;
+ }
+ if (bufferptr >= (block_size - 1) * 64 + buffer) {
+ for (size_t i = 0; i < (block_size - 2); i++) {
+ base64_decode_block(dst, buffer + i * 64);
+ dst += 48;
+ }
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
+ } else {
+ base64_decode_block(dst, buffer + (block_size - 2) * 64);
+ }
+ dst += 48;
+ std::memcpy(buffer, buffer + (block_size - 1) * 64,
+ 64); // 64 might be too much
+ bufferptr -= (block_size - 1) * 64;
+ }
+ }
+ }
+
+ char *buffer_start = buffer;
+ // Optimization note: if this is almost full, then it is worth our
+ // time, otherwise, we should just decode directly.
+ int last_block = (int)((bufferptr - buffer_start) % 64);
+ if (last_block != 0 && srcend - src + last_block >= 64) {
+
+ while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ *bufferptr = char(val);
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ bufferptr += (val <= 63);
+ src++;
+ }
+ }
+
+ for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, buffer_start);
+ } else {
+ base64_decode_block(dst, buffer_start);
+ }
+ dst += 48;
+ }
+ if ((bufferptr - buffer_start) % 64 != 0) {
+ while (buffer_start + 4 < bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 4);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ if (buffer_start + 4 <= bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ // we may have 1, 2 or 3 bytes left and we need to decode them so let us
+ // bring in src content
+ int leftover = int(bufferptr - buffer_start);
+ if (leftover > 0) {
+ while (leftover < 4 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ buffer_start[leftover] = char(val);
+ leftover += (val <= 63);
+ src++;
+ }
+
+ if (leftover == 1) {
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ if (leftover == 2) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ dst += 1;
+ } else if (leftover == 3) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6) +
+ (uint32_t(buffer_start[2]) << 1 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 2);
+ dst += 2;
+ } else {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+ dst += 3;
+ }
+ }
+ }
+ if (src < srcend + equalsigns) {
+ result r =
+ scalar::base64::base64_tail_decode(dst, src, srcend - src, options);
+ if (r.error == error_code::INVALID_BASE64_CHARACTER) {
+ r.count += size_t(src - srcinit);
+ return r;
+ } else {
+ r.count += size_t(dst - dstinit);
+ }
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ r.error = error_code::INVALID_BASE64_CHARACTER;
+ }
+ }
+ return r;
+ }
+ if(equalsigns > 0) {
+ if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)};
+ }
+ }
+ return {SUCCESS, size_t(dst - dstinit)};
+}
+/* end file src/haswell/avx2_base64.cpp */
+
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+
+/* begin file src/generic/buf_block_reader.h */
+namespace simdutf {
+namespace haswell {
+namespace {
+
+// Walks through a buffer in block-sized increments, loading the last part with spaces
+template<size_t STEP_SIZE>
+struct buf_block_reader {
+public:
+ simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len);
+ simdutf_really_inline size_t block_index();
+ simdutf_really_inline bool has_full_block() const;
+ simdutf_really_inline const uint8_t *full_block() const;
+ /**
+ * Get the last block, padded with spaces.
+ *
+ * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this
+ * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there
+ * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding.
+ *
+ * @return the number of effective characters in the last block.
+ */
+ simdutf_really_inline size_t get_remainder(uint8_t *dst) const;
+ simdutf_really_inline void advance();
+private:
+ const uint8_t *buf;
+ const size_t len;
+ const size_t lenminusstep;
+ size_t idx;
+};
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text_64(const uint8_t *text) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ in.store(reinterpret_cast<uint8_t*>(buf));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ if (buf[i] < ' ') { buf[i] = '_'; }
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+simdutf_unused static char * format_mask(uint64_t mask) {
+ static char *buf = reinterpret_cast<char*>(malloc(64 + 1));
+ for (size_t i=0; i<64; i++) {
+ buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
+ }
+ buf[64] = '\0';
+ return buf;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() { return idx; }
+
+template<size_t STEP_SIZE>
+simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
+ return idx < lenminusstep;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline const uint8_t *buf_block_reader<STEP_SIZE>::full_block() const {
+ return &buf[idx];
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
+ if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers
+ std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once.
+ std::memcpy(dst, buf + idx, len - idx);
+ return len - idx;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
+ idx += STEP_SIZE;
+}
+
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/buf_block_reader.h */
+/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_validation {
+
+using namespace simd;
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+ //
+ // Return nonzero if there are incomplete multibyte characters at the end of the block:
+ // e.g. if there is a 4-byte character, but it's 3 bytes from the end.
+ //
+ simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
+ // If the previous input's last 3 bytes match this, they're too short (they ended at EOF):
+ // ... 1111____ 111_____ 11______
+ static const uint8_t max_array[32] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1
+ };
+ const simd8<uint8_t> max_value(&max_array[sizeof(max_array)-sizeof(simd8<uint8_t>)]);
+ return input.gt_bits(max_value);
+ }
+
+ struct utf8_checker {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+ // The last input we received
+ simd8<uint8_t> prev_input_block;
+ // Whether the last input we received was incomplete (used for ASCII fast path)
+ simd8<uint8_t> prev_incomplete;
+
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+ // The only problem that can happen at EOF is that a multibyte character is too short
+ // or a byte value too large in the last bytes: check_special_cases only checks for bytes
+ // too large in the first of two bytes.
+ simdutf_really_inline void check_eof() {
+ // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't
+ // possibly finish them.
+ this->error |= this->prev_incomplete;
+ }
+
+ simdutf_really_inline void check_next_input(const simd8x64<uint8_t>& input) {
+ if(simdutf_likely(is_ascii(input))) {
+ this->error |= this->prev_incomplete;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ this->prev_incomplete = is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1]);
+ this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1];
+
+ }
+ }
+
+ // do not forget to call check_eof!
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // namespace utf8_validation
+
+using utf8_validation::utf8_checker;
+
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+/* begin file src/generic/utf8_validation/utf8_validator.h */
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_validation {
+
+/**
+ * Validates that the string is actual UTF-8.
+ */
+template<class checker>
+bool generic_validate_utf8(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ return !c.errors();
+}
+
+bool generic_validate_utf8(const char * input, size_t length) {
+ return generic_validate_utf8<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+/**
+ * Validates that the string is actual UTF-8 and stops on errors.
+ */
+template<class checker>
+result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ if(c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input + count), length - count);
+ res.count += count;
+ return res;
+ }
+ reader.advance();
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ if (c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input) + count, length - count);
+ res.count += count;
+ return res;
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_utf8_with_errors(const char * input, size_t length) {
+ return generic_validate_utf8_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+bool generic_validate_ascii(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ uint8_t blocks[64]{};
+ simd::simd8x64<uint8_t> running_or(blocks);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ running_or |= in;
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ running_or |= in;
+ return running_or.is_ascii();
+}
+
+bool generic_validate_ascii(const char * input, size_t length) {
+ return generic_validate_ascii<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ }
+ reader.advance();
+
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_ascii_with_errors(const char * input, size_t length) {
+ return generic_validate_ascii_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+} // namespace utf8_validation
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_validator.h */
+// transcoding from UTF-8 to UTF-16
+/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_utf16 {
+
+using namespace simd;
+
+template <endianness endian>
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char16_t* utf16_output) noexcept {
+ // The implementation is not specific to haswell and should be moved to the generic directory.
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ // this loop could be unrolled further. For example, we could process the mask
+ // far more than 64 bytes.
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // Slow path. We hope that the compiler will recognize that this is a slow path.
+ // Anything that is not a continuation mask is a 'leading byte', that is, the
+ // start of a new code point.
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ // The *start* of code points is not so useful, rather, we want the *end* of code points.
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times when using solely
+ // the slow/regular path, and at least four times if there are fast paths.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ //
+ // Thus we may allow convert_masked_utf8_to_utf16 to process
+ // more bytes at a time under a fast-path mode where 16 bytes
+ // are consumed at once (e.g., when encountering ASCII).
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(input + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(input + pos, size - pos, utf16_output);
+ return utf16_output - start;
+}
+
+} // namespace utf8_to_utf16
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_utf16 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+ template <endianness endian>
+ simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf16::convert<endian>(in + pos, size - pos, utf16_output);
+ if(howmany == 0) { return 0; }
+ utf16_output += howmany;
+ }
+ return utf16_output - start;
+ }
+
+ template <endianness endian>
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf16_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf16 namespace
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+// transcoding from UTF-8 to UTF-32
+/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_utf32 {
+
+using namespace simd;
+
+
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char32_t* utf32_output) noexcept {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ size_t max_starting_point = (pos + 64) - 12;
+ while(pos < max_starting_point) {
+ size_t consumed = convert_masked_utf8_to_utf32(input + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ }
+ }
+ utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output);
+ return utf32_output - start;
+}
+
+
+} // namespace utf8_to_utf32
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_utf32 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
+ if(howmany == 0) { return 0; }
+ utf32_output += howmany;
+ }
+ return utf32_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf32_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf32 namespace
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+// other functions
+/* begin file src/generic/utf8.h */
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8 {
+
+using namespace simd;
+
+simdutf_really_inline size_t count_code_points(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.gt(-65);
+ count += count_ones(utf8_continuation_mask);
+ }
+ return count + scalar::utf8::count_code_points(in + pos, size - pos);
+}
+
+simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ // We count one word for anything that is not a continuation (so
+ // leading bytes).
+ count += 64 - count_ones(utf8_continuation_mask);
+ int64_t utf8_4byte = input.gteq_unsigned(240);
+ count += count_ones(utf8_4byte);
+ }
+ return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
+}
+} // utf8 namespace
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8.h */
+/* begin file src/generic/utf16.h */
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf16 {
+
+template <endianness big_endian>
+simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
+ count += count_ones(not_pair) / 2;
+ }
+ return count + scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t ascii_mask = input.lteq(0x7F);
+ uint64_t twobyte_mask = input.lteq(0x7FF);
+ uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF);
+
+ size_t ascii_count = count_ones(ascii_mask) / 2;
+ size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2;
+ size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2;
+ size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2;
+ count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count;
+ }
+ return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) {
+ return count_code_points<big_endian>(in, size);
+}
+
+simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) {
+ size_t pos = 0;
+
+ while (pos < size/32*32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ input.swap_bytes();
+ input.store(reinterpret_cast<uint16_t *>(output));
+ pos += 32;
+ output += 32;
+ }
+
+ scalar::utf16::change_endianness_utf16(in + pos, size - pos, output);
+}
+
+} // utf16
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf16.h */
+
+
+// transcoding from UTF-8 to Latin 1
+/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte,
+// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else.
+//
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+ constexpr const uint8_t FORBIDDEN = 0xff;
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ FORBIDDEN,
+ // 1110____ ________ <three byte lead in byte 1>
+ FORBIDDEN,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ FORBIDDEN
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ FORBIDDEN,
+ // ____0101 ________
+ FORBIDDEN,
+ // ____011_ ________
+ FORBIDDEN,
+ FORBIDDEN,
+
+ // ____1___ ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ // ____1101 ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ this->error |= check_special_cases(input, prev1);
+ }
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
+ if(howmany == 0) { return 0; }
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ latin1_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, latin1_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace haswell
+} // namespace simdutf
+/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace haswell {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output);
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ }
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace haswell
+ // namespace simdutf
+/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+namespace simdutf {
+namespace haswell {
+
+simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ if (length % 2 == 0) {
+ return avx2_detect_encodings<utf8_validation::utf8_checker>(input, length);
+ } else {
+ if (implementation::validate_utf8(input, length)) {
+ return simdutf::encoding_type::UTF8;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ return haswell::utf8_validation::generic_validate_utf8(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ return haswell::utf8_validation::generic_validate_utf8_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return haswell::utf8_validation::generic_validate_ascii(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ return haswell::utf8_validation::generic_validate_ascii_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = avx2_validate_utf16<endianness::LITTLE>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::LITTLE>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = avx2_validate_utf16<endianness::BIG>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::BIG>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = avx2_validate_utf16_with_errors<endianness::LITTLE>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::LITTLE>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = avx2_validate_utf16_with_errors<endianness::BIG>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::BIG>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
+ const char32_t* tail = avx2_validate_utf32le(buf, len);
+ if (tail) {
+ return scalar::utf32::validate(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+ result res = avx2_validate_utf32le_with_errors(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char*, char*> ret = avx2_convert_latin1_to_utf8(buf, len, utf8_output);
+ size_t converted_chars = ret.second - utf8_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = avx2_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = avx2_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char*, char32_t*> ret = avx2_convert_latin1_to_utf32(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* input, size_t size,
+ char* latin1_output) const noexcept {
+ return utf8_to_latin1::convert_valid(input, size, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::BIG>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size,
+ char32_t* utf32_output) const noexcept {
+ return utf8_to_utf32::convert_valid(input, size, utf32_output);
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = haswell::avx2_convert_utf16_to_latin1<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = haswell::avx2_convert_utf16_to_latin1<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = avx2_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = avx2_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement a custom function
+ return convert_utf16be_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: implement a custom function
+ return convert_utf16le_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = haswell::avx2_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = haswell::avx2_convert_utf16_to_utf8<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = haswell::avx2_convert_utf16_to_utf8_with_errors<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = haswell::avx2_convert_utf16_to_utf8_with_errors<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16le_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16be_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = avx2_convert_utf32_to_utf8(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = avx2_convert_utf32_to_latin1(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = avx2_convert_utf32_to_latin1_with_errors(buf, len, latin1_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ return convert_utf32_to_latin1(buf,len,latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = haswell::avx2_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = haswell::avx2_convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = haswell::avx2_convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = haswell::avx2_convert_utf16_to_utf32_with_errors<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf32_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = avx2_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = avx2_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = haswell::avx2_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16le(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16be(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16le_to_utf32(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16be_to_utf32(buf, len, utf32_output);
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ utf16::change_endianness_utf16(input, length, output);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept {
+ return count_utf8(buf,len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept {
+ return scalar::utf16::latin1_length_from_utf16(length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept {
+ return scalar::utf32::latin1_length_from_utf32(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
+}
+
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf16_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::utf16_length_from_utf8(input, length);
+}
+
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf32_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char *input, size_t len) const noexcept {
+ const uint8_t *data = reinterpret_cast<const uint8_t *>(input);
+ size_t answer = len / sizeof(__m256i) * sizeof(__m256i);
+ size_t i = 0;
+ __m256i four_64bits = _mm256_setzero_si256();
+ while (i + sizeof(__m256i) <= len) {
+ __m256i runner = _mm256_setzero_si256();
+ // We can do up to 255 loops without overflow.
+ size_t iterations = (len - i) / sizeof(__m256i);
+ if (iterations > 255) {
+ iterations = 255;
+ }
+ size_t max_i = i + iterations * sizeof(__m256i) - sizeof(__m256i);
+ for (; i + 4*sizeof(__m256i) <= max_i; i += 4*sizeof(__m256i)) {
+ __m256i input1 = _mm256_loadu_si256((const __m256i *)(data + i));
+ __m256i input2 = _mm256_loadu_si256((const __m256i *)(data + i + sizeof(__m256i)));
+ __m256i input3 = _mm256_loadu_si256((const __m256i *)(data + i + 2*sizeof(__m256i)));
+ __m256i input4 = _mm256_loadu_si256((const __m256i *)(data + i + 3*sizeof(__m256i)));
+ __m256i input12 = _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input1),
+ _mm256_cmpgt_epi8(_mm256_setzero_si256(), input2));
+ __m256i input23 = _mm256_add_epi8(_mm256_cmpgt_epi8(_mm256_setzero_si256(), input3),
+ _mm256_cmpgt_epi8(_mm256_setzero_si256(), input4));
+ __m256i input1234 = _mm256_add_epi8(input12, input23);
+ runner = _mm256_sub_epi8(
+ runner, input1234);
+ }
+ for (; i <= max_i; i += sizeof(__m256i)) {
+ __m256i input_256_chunk = _mm256_loadu_si256((const __m256i *)(data + i));
+ runner = _mm256_sub_epi8(
+ runner, _mm256_cmpgt_epi8(_mm256_setzero_si256(), input_256_chunk));
+ }
+ four_64bits = _mm256_add_epi64(
+ four_64bits, _mm256_sad_epu8(runner, _mm256_setzero_si256()));
+ }
+ answer += _mm256_extract_epi64(four_64bits, 0) +
+ _mm256_extract_epi64(four_64bits, 1) +
+ _mm256_extract_epi64(four_64bits, 2) +
+ _mm256_extract_epi64(four_64bits, 3);
+ return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast<const char *>(data + i), len - i);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffffff80 = _mm256_set1_epi32((uint32_t)0xffffff80);
+ const __m256i v_fffff800 = _mm256_set1_epi32((uint32_t)0xfffff800);
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 8 <= length; pos += 8) {
+ __m256i in = _mm256_loadu_si256((__m256i*)(input + pos));
+ const __m256i ascii_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffffff80), v_00000000);
+ const __m256i one_two_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_fffff800), v_00000000);
+ const __m256i two_bytes_bytemask = _mm256_xor_si256(one_two_bytes_bytemask, ascii_bytes_bytemask);
+ const __m256i one_two_three_bytes_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const __m256i three_bytes_bytemask = _mm256_xor_si256(one_two_three_bytes_bytemask, one_two_bytes_bytemask);
+ const uint32_t ascii_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(ascii_bytes_bytemask));
+ const uint32_t two_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(two_bytes_bytemask));
+ const uint32_t three_bytes_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(three_bytes_bytemask));
+
+ size_t ascii_count = count_ones(ascii_bytes_bitmask) / 4;
+ size_t two_bytes_count = count_ones(two_bytes_bitmask) / 4;
+ size_t three_bytes_count = count_ones(three_bytes_bitmask) / 4;
+ count += 32 - 3*ascii_count - 2*two_bytes_count - three_bytes_count;
+ }
+ return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const __m256i v_00000000 = _mm256_setzero_si256();
+ const __m256i v_ffff0000 = _mm256_set1_epi32((uint32_t)0xffff0000);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 8 <= length; pos += 8) {
+ __m256i in = _mm256_loadu_si256((__m256i*)(input + pos));
+ const __m256i surrogate_bytemask = _mm256_cmpeq_epi32(_mm256_and_si256(in, v_ffff0000), v_00000000);
+ const uint32_t surrogate_bitmask = static_cast<uint32_t>(_mm256_movemask_epi8(surrogate_bytemask));
+ size_t surrogate_count = (32-count_ones(surrogate_bitmask))/4;
+ count += 8 + surrogate_count;
+ }
+ return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ if(options & base64_url) {
+ return encode_base64<base64_url>(output, input, length);
+ } else {
+ return encode_base64<base64_default>(output, input, length);
+ }
+}
+} // namespace haswell
+} // namespace simdutf
+
+/* begin file src/simdutf/haswell/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_HASWELL
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+
+#if SIMDUTF_GCC11ORMORE // workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105593
+SIMDUTF_POP_DISABLE_WARNINGS
+#endif // end of workaround
+/* end file src/simdutf/haswell/end.h */
+/* end file src/haswell/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_PPC64
+/* begin file src/ppc64/implementation.cpp */
+
+
+
+
+
+/* begin file src/simdutf/ppc64/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "ppc64"
+// #define SIMDUTF_IMPLEMENTATION ppc64
+/* end file src/simdutf/ppc64/begin.h */
+namespace simdutf {
+namespace ppc64 {
+namespace {
+#ifndef SIMDUTF_PPC64_H
+#error "ppc64.h must be included"
+#endif
+using namespace simd;
+
+
+simdutf_really_inline bool is_ascii(const simd8x64<uint8_t>& input) {
+ // careful: 0x80 is not ascii.
+ return input.reduce_or().saturating_sub(0b01111111u).bits_not_set_anywhere();
+}
+
+simdutf_unused simdutf_really_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0
+ // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
+ return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
+}
+
+simdutf_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
+ // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
+ return simd8<bool>(is_third_byte | is_fourth_byte);
+}
+
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+
+/* begin file src/generic/buf_block_reader.h */
+namespace simdutf {
+namespace ppc64 {
+namespace {
+
+// Walks through a buffer in block-sized increments, loading the last part with spaces
+template<size_t STEP_SIZE>
+struct buf_block_reader {
+public:
+ simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len);
+ simdutf_really_inline size_t block_index();
+ simdutf_really_inline bool has_full_block() const;
+ simdutf_really_inline const uint8_t *full_block() const;
+ /**
+ * Get the last block, padded with spaces.
+ *
+ * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this
+ * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there
+ * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding.
+ *
+ * @return the number of effective characters in the last block.
+ */
+ simdutf_really_inline size_t get_remainder(uint8_t *dst) const;
+ simdutf_really_inline void advance();
+private:
+ const uint8_t *buf;
+ const size_t len;
+ const size_t lenminusstep;
+ size_t idx;
+};
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text_64(const uint8_t *text) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ in.store(reinterpret_cast<uint8_t*>(buf));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ if (buf[i] < ' ') { buf[i] = '_'; }
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+simdutf_unused static char * format_mask(uint64_t mask) {
+ static char *buf = reinterpret_cast<char*>(malloc(64 + 1));
+ for (size_t i=0; i<64; i++) {
+ buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
+ }
+ buf[64] = '\0';
+ return buf;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() { return idx; }
+
+template<size_t STEP_SIZE>
+simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
+ return idx < lenminusstep;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline const uint8_t *buf_block_reader<STEP_SIZE>::full_block() const {
+ return &buf[idx];
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
+ if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers
+ std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once.
+ std::memcpy(dst, buf + idx, len - idx);
+ return len - idx;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
+ idx += STEP_SIZE;
+}
+
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/buf_block_reader.h */
+/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_validation {
+
+using namespace simd;
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+ //
+ // Return nonzero if there are incomplete multibyte characters at the end of the block:
+ // e.g. if there is a 4-byte character, but it's 3 bytes from the end.
+ //
+ simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
+ // If the previous input's last 3 bytes match this, they're too short (they ended at EOF):
+ // ... 1111____ 111_____ 11______
+ static const uint8_t max_array[32] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1
+ };
+ const simd8<uint8_t> max_value(&max_array[sizeof(max_array)-sizeof(simd8<uint8_t>)]);
+ return input.gt_bits(max_value);
+ }
+
+ struct utf8_checker {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+ // The last input we received
+ simd8<uint8_t> prev_input_block;
+ // Whether the last input we received was incomplete (used for ASCII fast path)
+ simd8<uint8_t> prev_incomplete;
+
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+ // The only problem that can happen at EOF is that a multibyte character is too short
+ // or a byte value too large in the last bytes: check_special_cases only checks for bytes
+ // too large in the first of two bytes.
+ simdutf_really_inline void check_eof() {
+ // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't
+ // possibly finish them.
+ this->error |= this->prev_incomplete;
+ }
+
+ simdutf_really_inline void check_next_input(const simd8x64<uint8_t>& input) {
+ if(simdutf_likely(is_ascii(input))) {
+ this->error |= this->prev_incomplete;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ this->prev_incomplete = is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1]);
+ this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1];
+
+ }
+ }
+
+ // do not forget to call check_eof!
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // namespace utf8_validation
+
+using utf8_validation::utf8_checker;
+
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+/* begin file src/generic/utf8_validation/utf8_validator.h */
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_validation {
+
+/**
+ * Validates that the string is actual UTF-8.
+ */
+template<class checker>
+bool generic_validate_utf8(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ return !c.errors();
+}
+
+bool generic_validate_utf8(const char * input, size_t length) {
+ return generic_validate_utf8<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+/**
+ * Validates that the string is actual UTF-8 and stops on errors.
+ */
+template<class checker>
+result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ if(c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input + count), length - count);
+ res.count += count;
+ return res;
+ }
+ reader.advance();
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ if (c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input) + count, length - count);
+ res.count += count;
+ return res;
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_utf8_with_errors(const char * input, size_t length) {
+ return generic_validate_utf8_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+bool generic_validate_ascii(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ uint8_t blocks[64]{};
+ simd::simd8x64<uint8_t> running_or(blocks);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ running_or |= in;
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ running_or |= in;
+ return running_or.is_ascii();
+}
+
+bool generic_validate_ascii(const char * input, size_t length) {
+ return generic_validate_ascii<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ }
+ reader.advance();
+
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_ascii_with_errors(const char * input, size_t length) {
+ return generic_validate_ascii_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+} // namespace utf8_validation
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_validator.h */
+// transcoding from UTF-8 to UTF-16
+/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_to_utf16 {
+
+using namespace simd;
+
+template <endianness endian>
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char16_t* utf16_output) noexcept {
+ // The implementation is not specific to haswell and should be moved to the generic directory.
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ // this loop could be unrolled further. For example, we could process the mask
+ // far more than 64 bytes.
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // Slow path. We hope that the compiler will recognize that this is a slow path.
+ // Anything that is not a continuation mask is a 'leading byte', that is, the
+ // start of a new code point.
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ // The *start* of code points is not so useful, rather, we want the *end* of code points.
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times when using solely
+ // the slow/regular path, and at least four times if there are fast paths.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ //
+ // Thus we may allow convert_masked_utf8_to_utf16 to process
+ // more bytes at a time under a fast-path mode where 16 bytes
+ // are consumed at once (e.g., when encountering ASCII).
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(input + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(input + pos, size - pos, utf16_output);
+ return utf16_output - start;
+}
+
+} // namespace utf8_to_utf16
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_to_utf16 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+ template <endianness endian>
+ simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf16::convert<endian>(in + pos, size - pos, utf16_output);
+ if(howmany == 0) { return 0; }
+ utf16_output += howmany;
+ }
+ return utf16_output - start;
+ }
+
+ template <endianness endian>
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf16_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf16 namespace
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+// transcoding from UTF-8 to UTF-32
+/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_to_utf32 {
+
+using namespace simd;
+
+
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char32_t* utf32_output) noexcept {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ size_t max_starting_point = (pos + 64) - 12;
+ while(pos < max_starting_point) {
+ size_t consumed = convert_masked_utf8_to_utf32(input + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ }
+ }
+ utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output);
+ return utf32_output - start;
+}
+
+
+} // namespace utf8_to_utf32
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8_to_utf32 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
+ if(howmany == 0) { return 0; }
+ utf32_output += howmany;
+ }
+ return utf32_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf32_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf32 namespace
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+// other functions
+/* begin file src/generic/utf8.h */
+
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf8 {
+
+using namespace simd;
+
+simdutf_really_inline size_t count_code_points(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.gt(-65);
+ count += count_ones(utf8_continuation_mask);
+ }
+ return count + scalar::utf8::count_code_points(in + pos, size - pos);
+}
+
+simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ // We count one word for anything that is not a continuation (so
+ // leading bytes).
+ count += 64 - count_ones(utf8_continuation_mask);
+ int64_t utf8_4byte = input.gteq_unsigned(240);
+ count += count_ones(utf8_4byte);
+ }
+ return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
+}
+} // utf8 namespace
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf8.h */
+/* begin file src/generic/utf16.h */
+namespace simdutf {
+namespace ppc64 {
+namespace {
+namespace utf16 {
+
+template <endianness big_endian>
+simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
+ count += count_ones(not_pair) / 2;
+ }
+ return count + scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t ascii_mask = input.lteq(0x7F);
+ uint64_t twobyte_mask = input.lteq(0x7FF);
+ uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF);
+
+ size_t ascii_count = count_ones(ascii_mask) / 2;
+ size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2;
+ size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2;
+ size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2;
+ count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count;
+ }
+ return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) {
+ return count_code_points<big_endian>(in, size);
+}
+
+simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) {
+ size_t pos = 0;
+
+ while (pos < size/32*32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ input.swap_bytes();
+ input.store(reinterpret_cast<uint16_t *>(output));
+ pos += 32;
+ output += 32;
+ }
+
+ scalar::utf16::change_endianness_utf16(in + pos, size - pos, output);
+}
+
+} // utf16
+} // unnamed namespace
+} // namespace ppc64
+} // namespace simdutf
+/* end file src/generic/utf16.h */
+
+//
+// Implementation-specific overrides
+//
+namespace simdutf {
+namespace ppc64 {
+
+simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ int out = 0;
+ if(validate_utf8(input, length)) { out |= encoding_type::UTF8; }
+ if((length % 2) == 0) {
+ if(validate_utf16(reinterpret_cast<const char16_t*>(input), length/2)) { out |= encoding_type::UTF16_LE; }
+ }
+ if((length % 4) == 0) {
+ if(validate_utf32(reinterpret_cast<const char32_t*>(input), length/4)) { out |= encoding_type::UTF32_LE; }
+ }
+
+ return out;
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ return ppc64::utf8_validation::generic_validate_utf8(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ return ppc64::utf8_validation::generic_validate_utf8_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return ppc64::utf8_validation::generic_validate_ascii(buf,len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ return ppc64::utf8_validation::generic_validate_ascii_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate<endianness::LITTLE>(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate<endianness::BIG>(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate_with_errors<endianness::LITTLE>(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf16::validate_with_errors<endianness::BIG>(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+ return scalar::utf32::validate_with_errors(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char16_t *buf, size_t len) const noexcept {
+ return scalar::utf32::validate(buf, len);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return result(error_code::OTHER, 0); // stub
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return result(error_code::OTHER, 0); // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* /*buf*/, size_t /*len*/, char16_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept {
+ return result(error_code::OTHER, 0); // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* /*buf*/, size_t /*len*/, char32_t* /*utf16_output*/) const noexcept {
+ return 0; // stub
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_valid<endianness::LITTLE>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf16_to_utf8::convert_valid<endianness::BIG>(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert(buf, len, utf8_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert_with_errors(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return scalar::utf32_to_utf8::convert_valid(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_valid<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return scalar::utf32_to_utf16::convert_valid<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert<endianness::BIG>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_valid<endianness::LITTLE>(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return scalar::utf16_to_utf32::convert_valid<endianness::BIG>(buf, len, utf32_output);
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ scalar::utf16::change_endianness_utf16(input, length, output);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::count_code_points<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::count_code_points<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf8_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return scalar::utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ return scalar::utf8::utf16_length_from_utf8(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ return scalar::utf32::utf8_length_from_utf32(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ return scalar::utf32::utf16_length_from_utf32(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return scalar::utf8::count_code_points(input, length);
+}
+
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ // skip trailing spaces
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ size_t equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};;
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ // skip trailing spaces
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ size_t equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return scalar::base64::binary_to_base64(input, length, output, options);
+}
+} // namespace ppc64
+} // namespace simdutf
+
+/* begin file src/simdutf/ppc64/end.h */
+/* end file src/simdutf/ppc64/end.h */
+/* end file src/ppc64/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_RVV
+/* begin file src/rvv/implementation.cpp */
+
+
+
+
+
+/* begin file src/simdutf/rvv/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "rvv"
+// #define SIMDUTF_IMPLEMENTATION rvv
+
+#if SIMDUTF_CAN_ALWAYS_RUN_RVV
+// nothing needed.
+#else
+SIMDUTF_TARGET_RVV
+#endif
+/* end file src/simdutf/rvv/begin.h */
+namespace simdutf {
+namespace rvv {
+namespace {
+#ifndef SIMDUTF_RVV_H
+#error "rvv.h must be included"
+#endif
+
+} // unnamed namespace
+} // namespace rvv
+} // namespace simdutf
+
+//
+// Implementation-specific overrides
+//
+namespace simdutf {
+namespace rvv {
+
+/* begin file src/rvv/rvv_length_from.inl.cpp */
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t *src, size_t len) const noexcept {
+ return utf32_length_from_utf16le(src, len);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t *src, size_t len) const noexcept {
+ return utf32_length_from_utf16be(src, len);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char *src, size_t len) const noexcept {
+ return utf32_length_from_utf8(src, len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char *src, size_t len) const noexcept {
+ return utf32_length_from_utf8(src, len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t len) const noexcept {
+ return len;
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t len) const noexcept {
+ return len;
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t len) const noexcept {
+ return len;
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t len) const noexcept {
+ return len;
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char *src, size_t len) const noexcept {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m8(len);
+ vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl);
+ vbool1_t mask = __riscv_vmsgt_vx_i8m8_b1(v, -65, vl);
+ count += __riscv_vcpop_m_b1(mask, vl);
+ }
+ return count;
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static size_t rvv_utf32_length_from_utf16(const char16_t *src, size_t len) {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ v = simdutf_byteflip<bflip>(v, vl);
+ vbool2_t notHigh = __riscv_vmor_mm_b2(
+ __riscv_vmsgtu_vx_u16m8_b2(v, 0xDFFF, vl),
+ __riscv_vmsltu_vx_u16m8_b2(v, 0xDC00, vl), vl);
+ count += __riscv_vcpop_m_b2(notHigh, vl);
+ }
+ return count;
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t *src, size_t len) const noexcept {
+ return rvv_utf32_length_from_utf16<simdutf_ByteFlip::NONE>(src, len);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t *src, size_t len) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf32_length_from_utf16<simdutf_ByteFlip::ZVBB>(src, len);
+ else
+ return rvv_utf32_length_from_utf16<simdutf_ByteFlip::V>(src, len);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char *src, size_t len) const noexcept {
+ size_t count = len;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m8(len);
+ vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl);
+ count += __riscv_vcpop_m_b1(__riscv_vmslt_vx_i8m8_b1(v, 0, vl), vl);
+ }
+ return count;
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static size_t rvv_utf8_length_from_utf16(const char16_t *src, size_t len) {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ v = simdutf_byteflip<bflip>(v, vl);
+ vbool2_t m234 = __riscv_vmsgtu_vx_u16m8_b2(v, 0x7F, vl);
+ vbool2_t m34 = __riscv_vmsgtu_vx_u16m8_b2(v, 0x7FF, vl);
+ vbool2_t notSur = __riscv_vmor_mm_b2(
+ __riscv_vmsltu_vx_u16m8_b2(v, 0xD800, vl),
+ __riscv_vmsgtu_vx_u16m8_b2(v, 0xDFFF, vl), vl);
+ vbool2_t m3 = __riscv_vmand_mm_b2(m34, notSur, vl);
+ count += vl + __riscv_vcpop_m_b2(m234, vl) + __riscv_vcpop_m_b2(m3, vl);
+ }
+ return count;
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t *src, size_t len) const noexcept {
+ return rvv_utf8_length_from_utf16<simdutf_ByteFlip::NONE>(src, len);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t *src, size_t len) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf8_length_from_utf16<simdutf_ByteFlip::ZVBB>(src, len);
+ else
+ return rvv_utf8_length_from_utf16<simdutf_ByteFlip::V>(src, len);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t *src, size_t len) const noexcept {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e32m8(len);
+ vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl);
+ vbool4_t m234 = __riscv_vmsgtu_vx_u32m8_b4(v, 0x7F, vl);
+ vbool4_t m34 = __riscv_vmsgtu_vx_u32m8_b4(v, 0x7FF, vl);
+ vbool4_t m4 = __riscv_vmsgtu_vx_u32m8_b4(v, 0xFFFF, vl);
+ count += vl + __riscv_vcpop_m_b4(m234, vl) + __riscv_vcpop_m_b4(m34, vl) + __riscv_vcpop_m_b4(m4, vl);
+ }
+ return count;
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char *src, size_t len) const noexcept {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m8(len);
+ vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl);
+ vbool1_t m1234 = __riscv_vmsgt_vx_i8m8_b1(v, -65, vl);
+ vbool1_t m4 = __riscv_vmsgtu_vx_u8m8_b1(
+ __riscv_vreinterpret_u8m8(v), (uint8_t)0b11101111, vl);
+ count += __riscv_vcpop_m_b1(m1234, vl) + __riscv_vcpop_m_b1(m4, vl);
+ }
+ return count;
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t *src, size_t len) const noexcept {
+ size_t count = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e32m8(len);
+ vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl);
+ vbool4_t m4 = __riscv_vmsgtu_vx_u32m8_b4(v, 0xFFFF, vl);
+ count += vl + __riscv_vcpop_m_b4(m4, vl);
+ }
+ return count;
+}
+
+/* end file src/rvv/rvv_length_from.inl.cpp */
+/* begin file src/rvv/rvv_validate.inl.cpp */
+
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *src, size_t len) const noexcept {
+ size_t vlmax = __riscv_vsetvlmax_e8m8();
+ vint8m8_t mask = __riscv_vmv_v_x_i8m8(0, vlmax);
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m8(len);
+ vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl);
+ mask = __riscv_vor_vv_i8m8_tu(mask, mask, v, vl);
+ }
+ return __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(mask, 0, vlmax), vlmax) < 0;
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *src, size_t len) const noexcept {
+ const char *beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m8(len);
+ vint8m8_t v = __riscv_vle8_v_i8m8((int8_t*)src, vl);
+ long idx = __riscv_vfirst_m_b1(__riscv_vmslt_vx_i8m8_b1(v, 0, vl), vl);
+ if (idx >= 0) return result(error_code::TOO_LARGE, src - beg + idx);
+ }
+ return result(error_code::SUCCESS, src - beg);
+}
+
+/* Returns a close estimation of the number of valid UTF-8 bytes up to the
+ * first invalid one, but never overestimating. */
+simdutf_really_inline static size_t rvv_count_valid_utf8(const char *src, size_t len) {
+ const char *beg = src;
+ if (len < 32) return 0;
+
+ /* validate first three bytes */
+ {
+ size_t idx = 3;
+ while (idx < len && (src[idx] >> 6) == 0b10)
+ ++idx;
+ if (idx > 3+3 || !scalar::utf8::validate(src, idx))
+ return 0;
+ }
+
+ static const uint64_t err1m[] = { 0x0202020202020202, 0x4915012180808080 };
+ static const uint64_t err2m[] = { 0xCBCBCB8B8383A3E7, 0xCBCBDBCBCBCBCBCB };
+ static const uint64_t err3m[] = { 0x0101010101010101, 0X01010101BABAAEE6 };
+
+ const vuint8m1_t err1tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err1m, 2));
+ const vuint8m1_t err2tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err2m, 2));
+ const vuint8m1_t err3tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err3m, 2));
+
+ size_t tail = 3;
+ size_t n = len - tail;
+
+ for (size_t vl; n > 0; n -= vl, src += vl) {
+ vl = __riscv_vsetvl_e8m4(n);
+ vuint8m4_t v0 = __riscv_vle8_v_u8m4((uint8_t const*)src, vl);
+
+ uint8_t next0 = src[vl+0];
+ uint8_t next1 = src[vl+1];
+ uint8_t next2 = src[vl+2];
+
+ /* fast path: ASCII */
+ if (__riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u8m4_b2(v0, 0b01111111, vl), vl) < 0 && (next0|next1|next2) < 0b10000000)
+ continue;
+
+ /* see "Validating UTF-8 In Less Than One Instruction Per Byte"
+ * https://arxiv.org/abs/2010.03090 */
+ vuint8m4_t v1 = __riscv_vslide1down_vx_u8m4(v0, next0, vl);
+ vuint8m4_t v2 = __riscv_vslide1down_vx_u8m4(v1, next1, vl);
+ vuint8m4_t v3 = __riscv_vslide1down_vx_u8m4(v2, next2, vl);
+
+ vuint8m4_t s1 = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vsrl_vx_u16m4(__riscv_vreinterpret_v_u8m4_u16m4(v2), 4, __riscv_vsetvlmax_e16m4()));
+ vuint8m4_t s3 = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vsrl_vx_u16m4(__riscv_vreinterpret_v_u8m4_u16m4(v3), 4, __riscv_vsetvlmax_e16m4()));
+
+ vuint8m4_t idx2 = __riscv_vand_vx_u8m4(v2, 0xF, vl);
+ vuint8m4_t idx1 = __riscv_vand_vx_u8m4(s1, 0xF, vl);
+ vuint8m4_t idx3 = __riscv_vand_vx_u8m4(s3, 0xF, vl);
+
+ vuint8m4_t err1 = simdutf_vrgather_u8m1x4(err1tbl, idx1);
+ vuint8m4_t err2 = simdutf_vrgather_u8m1x4(err2tbl, idx2);
+ vuint8m4_t err3 = simdutf_vrgather_u8m1x4(err3tbl, idx3);
+ vint8m4_t errs = __riscv_vreinterpret_v_u8m4_i8m4(__riscv_vand_vv_u8m4(__riscv_vand_vv_u8m4(err1, err2, vl), err3, vl));
+
+ vbool2_t is_3 = __riscv_vmsgtu_vx_u8m4_b2(v1, 0b11100000-1, vl);
+ vbool2_t is_4 = __riscv_vmsgtu_vx_u8m4_b2(v0, 0b11110000-1, vl);
+ vbool2_t is_34 = __riscv_vmor_mm_b2(is_3, is_4, vl);
+ vbool2_t err34 = __riscv_vmxor_mm_b2(is_34, __riscv_vmslt_vx_i8m4_b2(errs, 0, vl), vl);
+ vbool2_t errm = __riscv_vmor_mm_b2(__riscv_vmsgt_vx_i8m4_b2(errs, 0, vl), err34, vl);
+ if (__riscv_vfirst_m_b2(errm , vl) >= 0)
+ break;
+ }
+
+ /* we need to validate the last character */
+ while (tail < len && (src[0] >> 6) == 0b10) --src, ++tail;
+ return src - beg;
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *src, size_t len) const noexcept {
+ size_t count = rvv_count_valid_utf8(src, len);
+ return scalar::utf8::validate(src + count, len - count);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *src, size_t len) const noexcept {
+ size_t count = rvv_count_valid_utf8(src, len);
+ result res = scalar::utf8::validate_with_errors(src + count, len - count);
+ return result(res.error, count + res.count);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *src, size_t len) const noexcept {
+ return validate_utf16le_with_errors(src, len).error == error_code::SUCCESS;
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *src, size_t len) const noexcept {
+ return validate_utf16be_with_errors(src, len).error == error_code::SUCCESS;
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static result rvv_validate_utf16_with_errors(const char16_t *src, size_t len) {
+ const char16_t *beg = src;
+ uint16_t last = 0;
+ for (size_t vl; len > 0; len -= vl, src += vl, last = simdutf_byteflip<bflip>(src[-1])) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v1 = __riscv_vle16_v_u16m8((const uint16_t*)src, vl);
+ v1 = simdutf_byteflip<bflip>(v1, vl);
+ vuint16m8_t v0 = __riscv_vslide1up_vx_u16m8(v1, last, vl);
+
+ vbool2_t surhi = __riscv_vmseq_vx_u16m8_b2(__riscv_vand_vx_u16m8(v0, 0xFC00, vl), 0xD800, vl);
+ vbool2_t surlo = __riscv_vmseq_vx_u16m8_b2(__riscv_vand_vx_u16m8(v1, 0xFC00, vl), 0xDC00, vl);
+
+ long idx = __riscv_vfirst_m_b2(__riscv_vmxor_mm_b2(surhi, surlo, vl), vl);
+ if (idx >= 0) {
+ last = idx > 0 ? simdutf_byteflip<bflip>(src[idx-1]) : last;
+ return result(error_code::SURROGATE, src - beg + idx - (last - 0xD800u < 0x400u));
+ break;
+ }
+ }
+ if (last - 0xD800u < 0x400u)
+ return result(error_code::SURROGATE, src - beg - 1); /* end on high surrogate */
+ else
+ return result(error_code::SUCCESS, src - beg);
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *src, size_t len) const noexcept {
+ return rvv_validate_utf16_with_errors<simdutf_ByteFlip::NONE>(src, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *src, size_t len) const noexcept {
+ if (supports_zvbb())
+ return rvv_validate_utf16_with_errors<simdutf_ByteFlip::ZVBB>(src, len);
+ else
+ return rvv_validate_utf16_with_errors<simdutf_ByteFlip::V>(src, len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *src, size_t len) const noexcept {
+ size_t vlmax = __riscv_vsetvlmax_e32m8();
+ vuint32m8_t max = __riscv_vmv_v_x_u32m8(0x10FFFF, vlmax);
+ vuint32m8_t maxOff = __riscv_vmv_v_x_u32m8(0xFFFFF7FF, vlmax);
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e32m8(len);
+ vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl);
+ vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl);
+ max = __riscv_vmaxu_vv_u32m8_tu(max, max, v, vl);
+ maxOff = __riscv_vmaxu_vv_u32m8_tu(maxOff, maxOff, off, vl);
+ }
+ return __riscv_vfirst_m_b4(__riscv_vmor_mm_b4(
+ __riscv_vmsne_vx_u32m8_b4(max, 0x10FFFF, vlmax),
+ __riscv_vmsne_vx_u32m8_b4(maxOff, 0xFFFFF7FF, vlmax), vlmax), vlmax) < 0;
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *src, size_t len) const noexcept {
+ const char32_t *beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl) {
+ vl = __riscv_vsetvl_e32m8(len);
+ vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl);
+ vuint32m8_t off = __riscv_vadd_vx_u32m8(v, 0xFFFF2000, vl);
+ long idx;
+ idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(v, 0x10FFFF, vl), vl);
+ if (idx >= 0) return result(error_code::TOO_LARGE, src - beg + idx);
+ idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(off, 0xFFFFF7FF, vl), vl);
+ if (idx >= 0) return result(error_code::SURROGATE, src - beg + idx);
+ }
+ return result(error_code::SUCCESS, src - beg);
+}
+
+/* end file src/rvv/rvv_validate.inl.cpp */
+
+/* begin file src/rvv/rvv_latin1_to.inl.cpp */
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char *src, size_t len, char *dst) const noexcept {
+ char *beg = dst;
+ for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) {
+ vl = __riscv_vsetvl_e8m2(len);
+ vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl);
+ vbool4_t nascii = __riscv_vmslt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v1), 0, vl);
+ size_t cnt = __riscv_vcpop_m_b4(nascii, vl);
+ vlOut = vl + cnt;
+ if (cnt == 0) {
+ __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut);
+ continue;
+ }
+
+ vuint8m2_t v0 = __riscv_vor_vx_u8m2(__riscv_vsrl_vx_u8m2(v1, 6, vl), 0b11000000, vl);
+ v1 = __riscv_vand_vx_u8m2_mu(nascii, v1, v1, 0b10111111, vl);
+
+ vuint8m4_t wide = __riscv_vreinterpret_v_u16m4_u8m4(__riscv_vwmaccu_vx_u16m4(__riscv_vwaddu_vv_u16m4(v0, v1, vl), 0xFF, v1, vl));
+ vbool2_t mask = __riscv_vmsgtu_vx_u8m4_b2(__riscv_vsub_vx_u8m4(wide, 0b11000000, vl*2), 1, vl*2);
+ vuint8m4_t comp = __riscv_vcompress_vm_u8m4(wide, mask, vl*2);
+
+ __riscv_vse8_v_u8m4((uint8_t*)dst, comp, vlOut);
+ }
+ return dst - beg;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept {
+ char16_t *beg = dst;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e8m4(len);
+ vuint8m4_t v = __riscv_vle8_v_u8m4((uint8_t*)src, vl);
+ __riscv_vse16_v_u16m8((uint16_t*)dst, __riscv_vzext_vf2_u16m8(v, vl), vl);
+ }
+ return dst - beg;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept {
+ char16_t *beg = dst;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e8m4(len);
+ vuint8m4_t v = __riscv_vle8_v_u8m4((uint8_t*)src, vl);
+ __riscv_vse16_v_u16m8((uint16_t*)dst, __riscv_vsll_vx_u16m8(__riscv_vzext_vf2_u16m8(v, vl), 8, vl), vl);
+ }
+ return dst - beg;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept {
+ char32_t *beg = dst;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e8m2(len);
+ vuint8m2_t v = __riscv_vle8_v_u8m2((uint8_t*)src, vl);
+ __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf4_u32m8(v, vl), vl);
+ }
+ return dst - beg;
+}
+
+/* end file src/rvv/rvv_latin1_to.inl.cpp */
+/* begin file src/rvv/rvv_utf8_to.inl.cpp */
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static size_t rvv_utf32_store_utf16_m4(uint16_t *dst, vuint32m4_t utf32, size_t vl, vbool4_t m4even) {
+ /* convert [000000000000aaaa|aaaaaabbbbbbbbbb]
+ * to [110111bbbbbbbbbb|110110aaaaaaaaaa] */
+ vuint32m4_t sur = __riscv_vsub_vx_u32m4(utf32, 0x10000, vl);
+ sur = __riscv_vor_vv_u32m4(__riscv_vsll_vx_u32m4(sur, 16, vl),
+ __riscv_vsrl_vx_u32m4(sur, 10, vl), vl);
+ sur = __riscv_vand_vx_u32m4(sur, 0x3FF03FF, vl);
+ sur = __riscv_vor_vx_u32m4(sur, 0xDC00D800, vl);
+ /* merge 1 byte utf32 and 2 byte sur */
+ vbool8_t m4 = __riscv_vmsgtu_vx_u32m4_b8(utf32, 0xFFFF, vl);
+ vuint16m4_t utf32_16 = __riscv_vreinterpret_v_u32m4_u16m4(__riscv_vmerge_vvm_u32m4(utf32, sur, m4, vl));
+ /* compress and store */
+ vbool4_t mOut = __riscv_vmor_mm_b4(__riscv_vmsne_vx_u16m4_b4(utf32_16, 0, vl*2), m4even, vl*2);
+ vuint16m4_t vout = __riscv_vcompress_vm_u16m4(utf32_16, mOut, vl*2);
+ vl = __riscv_vcpop_m_b4(mOut, vl*2);
+ __riscv_vse16_v_u16m4(dst, simdutf_byteflip<bflip>(vout, vl), vl);
+ return vl;
+};
+
+template<typename Tdst, simdutf_ByteFlip bflip, bool validate=true>
+simdutf_really_inline static size_t rvv_utf8_to_common(char const *src, size_t len, Tdst *dst) {
+ static_assert(std::is_same<Tdst, uint16_t>() || std::is_same<Tdst, uint32_t>(), "invalid type");
+ constexpr bool is16 = std::is_same<Tdst, uint16_t>();
+ constexpr endianness endian = bflip == simdutf_ByteFlip::NONE ? endianness::LITTLE : endianness::BIG;
+ const auto scalar = [](char const *in, size_t count, Tdst *out) {
+ return is16 ? scalar::utf8_to_utf16::convert<endian>(in, count, (char16_t*)out)
+ : scalar::utf8_to_utf32::convert(in, count, (char32_t*)out);
+ };
+
+ if (len < 32) return scalar(src, len, dst);
+
+ /* validate first three bytes */
+ if (validate) {
+ size_t idx = 3;
+ while (idx < len && (src[idx] >> 6) == 0b10)
+ ++idx;
+ if (idx > 3+3 || !scalar::utf8::validate(src, idx))
+ return 0;
+ }
+
+ size_t tail = 3;
+ size_t n = len - tail;
+ Tdst *beg = dst;
+
+ static const uint64_t err1m[] = { 0x0202020202020202, 0x4915012180808080 };
+ static const uint64_t err2m[] = { 0xCBCBCB8B8383A3E7, 0xCBCBDBCBCBCBCBCB };
+ static const uint64_t err3m[] = { 0x0101010101010101, 0X01010101BABAAEE6 };
+
+ const vuint8m1_t err1tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err1m, 2));
+ const vuint8m1_t err2tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err2m, 2));
+ const vuint8m1_t err3tbl = __riscv_vreinterpret_v_u64m1_u8m1(__riscv_vle64_v_u64m1(err3m, 2));
+
+ size_t vl8m2 = __riscv_vsetvlmax_e8m2();
+ vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2);
+
+ for (size_t vl, vlOut; n > 0; n -= vl, src += vl, dst += vlOut) {
+ vl = __riscv_vsetvl_e8m2(n);
+
+ vuint8m2_t v0 = __riscv_vle8_v_u8m2((uint8_t const*)src, vl);
+ uint64_t max = __riscv_vmv_x_s_u8m1_u8(__riscv_vredmaxu_vs_u8m2_u8m1(v0, __riscv_vmv_s_x_u8m1(0, vl), vl));
+
+ uint8_t next0 = src[vl+0];
+ uint8_t next1 = src[vl+1];
+ uint8_t next2 = src[vl+2];
+
+ /* fast path: ASCII */
+ if ((max|next0|next1|next2) < 0b10000000) {
+ vlOut = vl;
+ if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip<bflip>(__riscv_vzext_vf2_u16m4(v0, vlOut), vlOut), vlOut);
+ else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf4_u32m8(v0, vlOut), vlOut);
+ continue;
+ }
+
+ /* see "Validating UTF-8 In Less Than One Instruction Per Byte"
+ * https://arxiv.org/abs/2010.03090 */
+ vuint8m2_t v1 = __riscv_vslide1down_vx_u8m2(v0, next0, vl);
+ vuint8m2_t v2 = __riscv_vslide1down_vx_u8m2(v1, next1, vl);
+ vuint8m2_t v3 = __riscv_vslide1down_vx_u8m2(v2, next2, vl);
+
+ if (validate) {
+ vuint8m2_t s1 = __riscv_vreinterpret_v_u16m2_u8m2(__riscv_vsrl_vx_u16m2(__riscv_vreinterpret_v_u8m2_u16m2(v2), 4, __riscv_vsetvlmax_e16m2()));
+ vuint8m2_t s3 = __riscv_vreinterpret_v_u16m2_u8m2(__riscv_vsrl_vx_u16m2(__riscv_vreinterpret_v_u8m2_u16m2(v3), 4, __riscv_vsetvlmax_e16m2()));
+
+ vuint8m2_t idx2 = __riscv_vand_vx_u8m2(v2, 0xF, vl);
+ vuint8m2_t idx1 = __riscv_vand_vx_u8m2(s1, 0xF, vl);
+ vuint8m2_t idx3 = __riscv_vand_vx_u8m2(s3, 0xF, vl);
+
+ vuint8m2_t err1 = simdutf_vrgather_u8m1x2(err1tbl, idx1);
+ vuint8m2_t err2 = simdutf_vrgather_u8m1x2(err2tbl, idx2);
+ vuint8m2_t err3 = simdutf_vrgather_u8m1x2(err3tbl, idx3);
+ vint8m2_t errs = __riscv_vreinterpret_v_u8m2_i8m2(__riscv_vand_vv_u8m2(__riscv_vand_vv_u8m2(err1, err2, vl), err3, vl));
+
+ vbool4_t is_3 = __riscv_vmsgtu_vx_u8m2_b4(v1, 0b11100000-1, vl);
+ vbool4_t is_4 = __riscv_vmsgtu_vx_u8m2_b4(v0, 0b11110000-1, vl);
+ vbool4_t is_34 = __riscv_vmor_mm_b4(is_3, is_4, vl);
+ vbool4_t err34 = __riscv_vmxor_mm_b4(is_34, __riscv_vmslt_vx_i8m2_b4(errs, 0, vl), vl);
+ vbool4_t errm = __riscv_vmor_mm_b4(__riscv_vmsgt_vx_i8m2_b4(errs, 0, vl), err34, vl);
+ if (__riscv_vfirst_m_b4(errm , vl) >= 0)
+ return 0;
+ }
+
+ /* decoding */
+
+ /* mask of non continuation bytes */
+ vbool4_t m = __riscv_vmsgt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v0), -65, vl);
+ vlOut = __riscv_vcpop_m_b4(m, vl);
+
+ /* extract first and second bytes */
+ vuint8m2_t b1 = __riscv_vcompress_vm_u8m2(v0, m, vl);
+ vuint8m2_t b2 = __riscv_vcompress_vm_u8m2(v1, m, vl);
+
+ /* fast path: one and two byte */
+ if (max < 0b11100000) {
+ b2 = __riscv_vand_vx_u8m2(b2, 0b00111111, vlOut);
+
+ vbool4_t m1 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b10111111, vlOut);
+ b1 = __riscv_vand_vx_u8m2_mu(m1, b1, b1, 63, vlOut);
+
+ vuint16m4_t b12 = __riscv_vwmulu_vv_u16m4(b1, __riscv_vmerge_vxm_u8m2(__riscv_vmv_v_x_u8m2(1, vlOut), 1<<6, m1, vlOut), vlOut);
+ b12 = __riscv_vwaddu_wv_u16m4_mu(m1, b12, b12, b2, vlOut);
+ if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip<bflip>(b12, vlOut), vlOut);
+ else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf2_u32m8(b12, vlOut), vlOut);
+ continue;
+ }
+
+ /* fast path: one, two and three byte */
+ if (max < 0b11110000) {
+ vuint8m2_t b3 = __riscv_vcompress_vm_u8m2(v2, m, vl);
+
+ b2 = __riscv_vand_vx_u8m2(b2, 0b00111111, vlOut);
+ b3 = __riscv_vand_vx_u8m2(b3, 0b00111111, vlOut);
+
+ vbool4_t m1 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b10111111, vlOut);
+ vbool4_t m3 = __riscv_vmsgtu_vx_u8m2_b4(b1, 0b11011111, vlOut);
+
+ vuint8m2_t t1 = __riscv_vand_vx_u8m2_mu(m1, b1, b1, 63, vlOut);
+ b1 = __riscv_vand_vx_u8m2_mu(m3, t1, b1, 15, vlOut);
+
+ vuint16m4_t b12 = __riscv_vwmulu_vv_u16m4(b1, __riscv_vmerge_vxm_u8m2(__riscv_vmv_v_x_u8m2(1, vlOut), 1<<6, m1, vlOut), vlOut);
+ b12 = __riscv_vwaddu_wv_u16m4_mu(m1, b12, b12, b2, vlOut);
+ vuint16m4_t b123 = __riscv_vwaddu_wv_u16m4_mu(m3, b12, __riscv_vsll_vx_u16m4_mu(m3, b12, b12, 6, vlOut), b3, vlOut);
+ if (is16) __riscv_vse16_v_u16m4((uint16_t*)dst, simdutf_byteflip<bflip>(b123, vlOut), vlOut);
+ else __riscv_vse32_v_u32m8((uint32_t*)dst, __riscv_vzext_vf2_u32m8(b123, vlOut), vlOut);
+ continue;
+ }
+
+ /* extract third and fourth bytes */
+ vuint8m2_t b3 = __riscv_vcompress_vm_u8m2(v2, m, vl);
+ vuint8m2_t b4 = __riscv_vcompress_vm_u8m2(v3, m, vl);
+
+ #define SIMDUTF_RVV_UTF8_TO_COMMON_M1(idx) \
+ vuint8m1_t c1 = __riscv_vget_v_u8m2_u8m1(b1, idx); \
+ vuint8m1_t c2 = __riscv_vget_v_u8m2_u8m1(b2, idx); \
+ vuint8m1_t c3 = __riscv_vget_v_u8m2_u8m1(b3, idx); \
+ vuint8m1_t c4 = __riscv_vget_v_u8m2_u8m1(b4, idx); \
+ /* remove prefix from trailing bytes */ \
+ c2 = __riscv_vand_vx_u8m1(c2, 0b00111111, vlOut); \
+ c3 = __riscv_vand_vx_u8m1(c3, 0b00111111, vlOut); \
+ c4 = __riscv_vand_vx_u8m1(c4, 0b00111111, vlOut); \
+ /* remove prefix from leading bytes
+ *
+ * We could also use vrgather here, but it increases register pressure,
+ * and its performance varies widely on current platforms. It might be
+ * worth reconsidering, though, once there is more hardware available.
+ * Same goes for the __riscv_vsrl_vv_u32m4 correction step.
+ *
+ * We shift left and then right by the number of bytes in the prefix,
+ * which can be calculated as follows:
+ * x max(x-10, 0)
+ * 0xxx -> 0000-0111 -> sift by 0 or 1 -> 0
+ * 10xx -> 1000-1011 -> don't care
+ * 110x -> 1100,1101 -> sift by 3 -> 2,3
+ * 1110 -> 1110 -> sift by 4 -> 4
+ * 1111 -> 1111 -> sift by 5 -> 5
+ *
+ * vssubu.vx v, 10, (max(x-10, 0)) almost gives us what we want, we
+ * just need to manually detect and handle the one special case:
+ */ \
+ vuint8m1_t shift = __riscv_vsrl_vx_u8m1(c1, 4, vlOut); \
+ shift = __riscv_vmerge_vxm_u8m1(__riscv_vssubu_vx_u8m1(shift, 10, vlOut), 3, __riscv_vmseq_vx_u8m1_b8(shift, 12, vlOut), vlOut); \
+ c1 = __riscv_vsll_vv_u8m1(c1, shift, vlOut); \
+ c1 = __riscv_vsrl_vv_u8m1(c1, shift, vlOut); \
+ /* unconditionally widen and combine to c1234 */ \
+ vuint16m2_t c34 = __riscv_vwaddu_wv_u16m2(__riscv_vwmulu_vx_u16m2(c3, 1<<6, vlOut), c4, vlOut); \
+ vuint16m2_t c12 = __riscv_vwaddu_wv_u16m2(__riscv_vwmulu_vx_u16m2(c1, 1<<6, vlOut), c2, vlOut); \
+ vuint32m4_t c1234 = __riscv_vwaddu_wv_u32m4(__riscv_vwmulu_vx_u32m4(c12, 1 << 12, vlOut), c34, vlOut); \
+ /* derive required right-shift amount from `shift` to reduce
+ * c1234 to the required number of bytes */ \
+ c1234 = __riscv_vsrl_vv_u32m4(c1234, __riscv_vzext_vf4_u32m4(__riscv_vmul_vx_u8m1( \
+ __riscv_vrsub_vx_u8m1( __riscv_vssubu_vx_u8m1(shift, 2, vlOut), 3, vlOut), 6, vlOut), vlOut), vlOut); \
+ /* store result in desired format */ \
+ if (is16) vlDst = rvv_utf32_store_utf16_m4<bflip>((uint16_t*)dst, c1234, vlOut, m4even); \
+ else vlDst = vlOut, __riscv_vse32_v_u32m4((uint32_t*)dst, c1234, vlOut);
+
+ /* Unrolling this manually reduces register pressure and allows
+ * us to terminate early. */
+ {
+ size_t vlOutm2 = vlOut, vlDst;
+ vlOut = __riscv_vsetvl_e8m1(vlOut);
+ SIMDUTF_RVV_UTF8_TO_COMMON_M1(0)
+ if (vlOutm2 == vlOut) {
+ vlOut = vlDst;
+ continue;
+ }
+
+ dst += vlDst;
+ vlOut = vlOutm2 - vlOut;
+ }
+ {
+ size_t vlDst;
+ SIMDUTF_RVV_UTF8_TO_COMMON_M1(1)
+ vlOut = vlDst;
+ }
+
+#undef SIMDUTF_RVV_UTF8_TO_COMMON_M1
+ }
+
+ /* validate the last character and reparse it + tail */
+ if (len > tail) {
+ if ((src[0] >> 6) == 0b10)
+ --dst;
+ while ((src[0] >> 6) == 0b10 && tail < len)
+ --src, ++tail;
+ if (is16) {
+ /* go back one more, when on high surrogate */
+ if (simdutf_byteflip<bflip>((uint16_t)dst[-1]) >= 0xD800 && simdutf_byteflip<bflip>((uint16_t)dst[-1]) <= 0xDBFF)
+ --dst;
+ }
+ }
+ size_t ret = scalar(src, tail, dst);
+ if (ret == 0) return 0;
+ return (size_t)(dst - beg) + ret;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char *src, size_t len, char *dst) const noexcept {
+ const char *beg = dst;
+ uint8_t last = 0b10000000;
+ for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut, last = src[-1]) {
+ vl = __riscv_vsetvl_e8m2(len);
+ vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl);
+ vbool4_t m = __riscv_vmsltu_vx_u8m2_b4(v1, 0b11000000, vl);
+ vlOut = __riscv_vcpop_m_b4(m, vl);
+ if (vlOut != vl || last > 0b01111111) {
+ vuint8m2_t v0 = __riscv_vslide1up_vx_u8m2(v1, last, vl);
+
+ vbool4_t leading0 = __riscv_vmsgtu_vx_u8m2_b4(v0, 0b10111111, vl);
+ vbool4_t trailing1 = __riscv_vmslt_vx_i8m2_b4(__riscv_vreinterpret_v_u8m2_i8m2(v1), (uint8_t)0b11000000, vl);
+ vbool4_t tobig = __riscv_vmand_mm_b4(leading0, __riscv_vmsgtu_vx_u8m2_b4(__riscv_vxor_vx_u8m2(v0, (uint8_t)-62, vl), 1, vl), vl);
+ if (__riscv_vfirst_m_b4(__riscv_vmor_mm_b4(tobig, __riscv_vmxor_mm_b4(leading0, trailing1, vl), vl), vl) >= 0)
+ return 0;
+
+ v1 = __riscv_vor_vx_u8m2_mu(__riscv_vmseq_vx_u8m2_b4(v0, 0b11000011, vl), v1, v1, 0b01000000, vl);
+ v1 = __riscv_vcompress_vm_u8m2(v1, m, vl);
+ }
+ __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut);
+ }
+ if (last > 0b10111111)
+ return 0;
+ return dst - beg;
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char *src, size_t len, char *dst) const noexcept {
+ size_t res = convert_utf8_to_latin1(src, len, dst);
+ if (res) return result(error_code::SUCCESS, res);
+ return scalar::utf8_to_latin1::convert_with_errors(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char *src, size_t len, char *dst) const noexcept {
+ const char *beg = dst;
+ uint8_t last = 0b11000000;
+ for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut, last = src[-1]) {
+ vl = __riscv_vsetvl_e8m2(len);
+ vuint8m2_t v1 = __riscv_vle8_v_u8m2((uint8_t*)src, vl);
+ vbool4_t m = __riscv_vmsltu_vx_u8m2_b4(v1, 0b11000000, vl);
+ vlOut = __riscv_vcpop_m_b4(m, vl);
+ if (vlOut != vl || last > 0b01111111) {
+ vuint8m2_t v0 = __riscv_vslide1up_vx_u8m2(v1, last, vl);
+ v1 = __riscv_vor_vx_u8m2_mu(__riscv_vmseq_vx_u8m2_b4(v0, 0b11000011, vl), v1, v1, 0b01000000, vl);
+ v1 = __riscv_vcompress_vm_u8m2(v1, m, vl);
+ }
+ __riscv_vse8_v_u8m2((uint8_t*)dst, v1, vlOut);
+ }
+ return dst - beg;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept {
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::NONE>(src, len, (uint16_t*)dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::ZVBB>(src, len, (uint16_t*)dst);
+ else
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::V>(src, len, (uint16_t*)dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char *src, size_t len, char16_t *dst) const noexcept {
+ size_t res = convert_utf8_to_utf16le(src, len, dst);
+ if (res) return result(error_code::SUCCESS, res);
+ return scalar::utf8_to_utf16::convert_with_errors<endianness::LITTLE>(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char *src, size_t len, char16_t *dst) const noexcept {
+ size_t res = convert_utf8_to_utf16be(src, len, dst);
+ if (res) return result(error_code::SUCCESS, res);
+ return scalar::utf8_to_utf16::convert_with_errors<endianness::BIG>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char *src, size_t len, char16_t *dst) const noexcept {
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::NONE, false>(src, len, (uint16_t*)dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char *src, size_t len, char16_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::ZVBB, false>(src, len, (uint16_t*)dst);
+ else
+ return rvv_utf8_to_common<uint16_t, simdutf_ByteFlip::V, false>(src, len, (uint16_t*)dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept {
+ return rvv_utf8_to_common<uint32_t, simdutf_ByteFlip::NONE>(src, len, (uint32_t*)dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char *src, size_t len, char32_t *dst) const noexcept {
+ size_t res = convert_utf8_to_utf32(src, len, dst);
+ if (res) return result(error_code::SUCCESS, res);
+ return scalar::utf8_to_utf32::convert_with_errors(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char *src, size_t len, char32_t *dst) const noexcept {
+ return rvv_utf8_to_common<uint32_t, simdutf_ByteFlip::NONE, false>(src, len, (uint32_t*)dst);
+}
+
+/* end file src/rvv/rvv_utf8_to.inl.cpp */
+/* begin file src/rvv/rvv_utf16_to.inl.cpp */
+#include <cstdio>
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static result rvv_utf16_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) {
+ const char16_t *const beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ v = simdutf_byteflip<bflip>(v, vl);
+ long idx = __riscv_vfirst_m_b2(__riscv_vmsgtu_vx_u16m8_b2(v, 255, vl), vl);
+ if (idx >= 0)
+ return result(error_code::TOO_LARGE, beg - src + idx);
+ __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m4(v, vl), vl);
+ }
+ return result(error_code::SUCCESS, src - beg);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf16le_to_latin1_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf16be_to_latin1_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) const noexcept {
+ return rvv_utf16_to_latin1_with_errors<simdutf_ByteFlip::NONE>(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t *src, size_t len, char *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf16_to_latin1_with_errors<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_utf16_to_latin1_with_errors<simdutf_ByteFlip::V>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept {
+ const char16_t *const beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m4(v, vl), vl);
+ }
+ return src - beg;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t *src, size_t len, char *dst) const noexcept {
+ const char16_t *const beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ __riscv_vse8_v_u8m4((uint8_t*)dst, __riscv_vnsrl_wx_u8m4(v, 8, vl), vl);
+ }
+ return src - beg;
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static result rvv_utf16_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) {
+ size_t n = len;
+ const char16_t *srcBeg = src;
+ const char *dstBeg = dst;
+ size_t vl8m4 = __riscv_vsetvlmax_e8m4();
+ vbool2_t m4mulp2 = __riscv_vmseq_vx_u8m4_b2(__riscv_vand_vx_u8m4(__riscv_vid_v_u8m4(vl8m4), 3, vl8m4), 2, vl8m4);
+
+ for (size_t vl, vlOut; n > 0; ) {
+ vl = __riscv_vsetvl_e16m2(n);
+
+ vuint16m2_t v = __riscv_vle16_v_u16m2((uint16_t const*)src, vl);
+ v = simdutf_byteflip<bflip>(v, vl);
+ vbool8_t m234 = __riscv_vmsgtu_vx_u16m2_b8(v, 0x80-1, vl);
+
+ if (__riscv_vfirst_m_b8(m234,vl) < 0) { /* 1 byte utf8 */
+ vlOut = vl;
+ __riscv_vse8_v_u8m1((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m1(v, vlOut), vlOut);
+ n -= vl, src += vl, dst += vlOut;
+ continue;
+ }
+
+ vbool8_t m34 = __riscv_vmsgtu_vx_u16m2_b8(v, 0x800-1, vl);
+
+ if (__riscv_vfirst_m_b8(m34,vl) < 0) { /* 1/2 byte utf8 */
+ /* 0: [ aaa|aabbbbbb]
+ * 1: [aabbbbbb| ] vsll 8
+ * 2: [ | aaaaa] vsrl 6
+ * 3: [00111111|00011111]
+ * 4: [ bbbbbb|000aaaaa] (1|2)&3
+ * 5: [11000000|11000000]
+ * 6: [10bbbbbb|110aaaaa] 4|5 */
+ vuint16m2_t twoByte =
+ __riscv_vand_vx_u16m2(__riscv_vor_vv_u16m2(
+ __riscv_vsll_vx_u16m2(v, 8, vl),
+ __riscv_vsrl_vx_u16m2(v, 6, vl),
+ vl), 0b0011111100011111, vl);
+ vuint16m2_t vout16 = __riscv_vor_vx_u16m2_mu(m234, v, twoByte, 0b1000000011000000, vl);
+ vuint8m2_t vout = __riscv_vreinterpret_v_u16m2_u8m2(vout16);
+
+ /* Every high byte that is zero should be compressed
+ * low bytes should never be compressed, so we set them
+ * to all ones, and then create a non-zero bytes mask */
+ vbool4_t mcomp = __riscv_vmsne_vx_u8m2_b4(__riscv_vreinterpret_v_u16m2_u8m2(__riscv_vor_vx_u16m2(vout16, 0xFF, vl)), 0, vl*2);
+ vlOut = __riscv_vcpop_m_b4(mcomp, vl*2);
+
+ vout = __riscv_vcompress_vm_u8m2(vout, mcomp, vl*2);
+ __riscv_vse8_v_u8m2((uint8_t*)dst, vout, vlOut);
+
+ n -= vl, src += vl, dst += vlOut;
+ continue;
+ }
+
+ vbool8_t sur = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v, 0xF800, vl), 0xD800, vl);
+ long first = __riscv_vfirst_m_b8(sur, vl);
+ size_t tail = vl - first;
+ vl = first < 0 ? vl : first;
+
+ if (vl > 0) { /* 1/2/3 byte utf8 */
+ /* in: [aaaabbbb|bbcccccc]
+ * v1: [0bcccccc| ] vsll 8
+ * v1: [10cccccc| ] vsll 8 & 0b00111111 | 0b10000000
+ * v2: [ |110bbbbb] vsrl 6 & 0b00111111 | 0b11000000
+ * v2: [ |10bbbbbb] vsrl 6 & 0b00111111 | 0b10000000
+ * v3: [ |1110aaaa] vsrl 12 | 0b11100000
+ * 1: [00000000|0bcccccc|00000000|00000000] => [0bcccccc]
+ * 2: [00000000|10cccccc|110bbbbb|00000000] => [110bbbbb] [10cccccc]
+ * 3: [00000000|10cccccc|10bbbbbb|1110aaaa] => [1110aaaa] [10bbbbbb] [10cccccc]
+ */
+ vuint16m2_t v1, v2, v3, v12;
+ v1 = __riscv_vor_vx_u16m2_mu(m234, v, __riscv_vand_vx_u16m2(v, 0b00111111, vl), 0b10000000, vl);
+ v1 = __riscv_vsll_vx_u16m2(v1, 8, vl);
+
+ v2 = __riscv_vor_vx_u16m2(__riscv_vand_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 6, vl), 0b00111111, vl), 0b10000000, vl);
+ v2 = __riscv_vor_vx_u16m2_mu(__riscv_vmnot_m_b8(m34,vl), v2, v2, 0b01000000, vl);
+ v3 = __riscv_vor_vx_u16m2(__riscv_vsrl_vx_u16m2(v, 12, vl), 0b11100000, vl);
+ v12 = __riscv_vor_vv_u16m2_mu(m234, v1, v1, v2, vl);
+
+ vuint32m4_t w12 = __riscv_vwmulu_vx_u32m4(v12, 1<<8, vl);
+ vuint32m4_t w123 = __riscv_vwaddu_wv_u32m4_mu(m34, w12, w12, v3, vl);
+ vuint8m4_t vout = __riscv_vreinterpret_v_u32m4_u8m4(w123);
+
+ vbool2_t mcomp = __riscv_vmor_mm_b2(m4mulp2, __riscv_vmsne_vx_u8m4_b2(vout, 0, vl*4), vl*4);
+ vlOut = __riscv_vcpop_m_b2(mcomp, vl*4);
+
+ vout = __riscv_vcompress_vm_u8m4(vout, mcomp, vl*4);
+ __riscv_vse8_v_u8m4((uint8_t*)dst, vout, vlOut);
+
+ n -= vl, src += vl, dst += vlOut;
+ }
+
+ if (tail) while (n) {
+ uint16_t word = simdutf_byteflip<bflip>(src[0]);
+ if((word & 0xFF80)==0) {
+ break;
+ } else if((word & 0xF800)==0) {
+ break;
+ } else if ((word & 0xF800) != 0xD800) {
+ break;
+ } else {
+ // must be a surrogate pair
+ if (n <= 1) return result(error_code::SURROGATE, src - srcBeg);
+ uint16_t diff = word - 0xD800;
+ if (diff > 0x3FF) return result(error_code::SURROGATE, src - srcBeg);
+ uint16_t diff2 = simdutf_byteflip<bflip>(src[1]) - 0xDC00;
+ if (diff2 > 0x3FF) return result(error_code::SURROGATE, src - srcBeg);
+
+ uint32_t value = ((diff + 0x40) << 10) + diff2 ;
+
+ // will generate four UTF-8 bytes
+ // we have 0b11110XXX 0b10XXXXXX 0b10XXXXXX 0b10XXXXXX
+ *dst++ = (char)( (value>>18) | 0b11110000);
+ *dst++ = (char)(((value>>12) & 0b111111) | 0b10000000);
+ *dst++ = (char)(((value>> 6) & 0b111111) | 0b10000000);
+ *dst++ = (char)(( value & 0b111111) | 0b10000000);
+ src += 2;
+ n -= 2;
+ }
+ }
+ }
+
+ return result(error_code::SUCCESS, dst - dstBeg);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf16le_to_utf8_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf16be_to_utf8_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) const noexcept {
+ return rvv_utf16_to_utf8_with_errors<simdutf_ByteFlip::NONE>(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t *src, size_t len, char *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf16_to_utf8_with_errors<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_utf16_to_utf8_with_errors<simdutf_ByteFlip::V>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept {
+ return convert_utf16le_to_utf8(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t *src, size_t len, char *dst) const noexcept {
+ return convert_utf16be_to_utf8(src, len, dst);
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static result rvv_utf16_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) {
+ const char16_t *const srcBeg = src;
+ char32_t *const dstBeg = dst;
+
+ constexpr const uint16_t ANY_SURROGATE_MASK = 0xf800;
+ constexpr const uint16_t ANY_SURROGATE_VALUE = 0xd800;
+ constexpr const uint16_t LO_SURROGATE_MASK = 0xfc00;
+ constexpr const uint16_t LO_SURROGATE_VALUE = 0xdc00;
+ constexpr const uint16_t HI_SURROGATE_MASK = 0xfc00;
+ constexpr const uint16_t HI_SURROGATE_VALUE = 0xd800;
+
+ uint16_t last = 0;
+ while (len > 0) {
+ size_t vl = __riscv_vsetvl_e16m2(len);
+ vuint16m2_t v0 = __riscv_vle16_v_u16m2((uint16_t const*)src, vl);
+ v0 = simdutf_byteflip<bflip>(v0, vl);
+
+ { // check fast-path
+ const vuint16m2_t v = __riscv_vand_vx_u16m2(v0, ANY_SURROGATE_MASK, vl);
+ const vbool8_t any_surrogate = __riscv_vmseq_vx_u16m2_b8(v, ANY_SURROGATE_VALUE, vl);
+ if (__riscv_vfirst_m_b8(any_surrogate, vl) < 0) {
+ /* no surrogates */
+ __riscv_vse32_v_u32m4((uint32_t*)dst, __riscv_vzext_vf2_u32m4(v0, vl), vl);
+ len -= vl;
+ src += vl;
+ dst += vl;
+ continue;
+ }
+ }
+
+ if ((simdutf_byteflip<bflip>(src[0]) & LO_SURROGATE_MASK) == LO_SURROGATE_VALUE) {
+ return result(error_code::SURROGATE, src - srcBeg);
+ }
+
+ // decode surrogates
+ vuint16m2_t v1 = __riscv_vslide1down_vx_u16m2(v0, 0, vl);
+ vl = __riscv_vsetvl_e16m2(vl - 1);
+ if (vl == 0) {
+ return result(error_code::SURROGATE, src - srcBeg);
+ }
+
+ const vbool8_t surhi = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v0, HI_SURROGATE_MASK, vl), HI_SURROGATE_VALUE, vl);
+ const vbool8_t surlo = __riscv_vmseq_vx_u16m2_b8(__riscv_vand_vx_u16m2(v1, LO_SURROGATE_MASK, vl), LO_SURROGATE_VALUE, vl);
+
+ // compress everything but lo surrogates
+ const vbool8_t compress = __riscv_vmsne_vx_u16m2_b8(__riscv_vand_vx_u16m2(v0, LO_SURROGATE_MASK, vl), LO_SURROGATE_VALUE, vl);
+
+ {
+ const vbool8_t diff = __riscv_vmxor_mm_b8(surhi, surlo, vl);
+ const long idx = __riscv_vfirst_m_b8(diff, vl);
+ if (idx >= 0) {
+ return result(error_code::SURROGATE, src - srcBeg + idx + 1);
+ }
+ }
+
+ last = simdutf_byteflip<bflip>(src[vl]);
+ vuint32m4_t utf32 = __riscv_vzext_vf2_u32m4(v0, vl);
+
+ // v0 = 110110yyyyyyyyyy (0xd800 + yyyyyyyyyy) --- hi surrogate
+ // v1 = 110111xxxxxxxxxx (0xdc00 + xxxxxxxxxx) --- lo surrogate
+
+ // t0 = u16( 0000_00yy_yyyy_yyyy)
+ const vuint32m4_t t0 = __riscv_vzext_vf2_u32m4(__riscv_vand_vx_u16m2(v0, 0x03ff, vl), vl);
+ // t1 = u32(0000_0000_0000_yyyy_yyyy_yy00_0000_0000)
+ const vuint32m4_t t1 = __riscv_vsll_vx_u32m4(t0, 10, vl);
+
+ // t2 = u32(0000_0000_0000_0000_0000_00xx_xxxx_xxxx)
+ const vuint32m4_t t2 = __riscv_vzext_vf2_u32m4(__riscv_vand_vx_u16m2(v1, 0x03ff, vl), vl);
+
+ // t3 = u32(0000_0000_0000_yyyy_yyyy_yyxx_xxxx_xxxx)
+ const vuint32m4_t t3 = __riscv_vor_vv_u32m4(t1, t2, vl);
+
+ // t4 = utf32 from surrogate pairs
+ const vuint32m4_t t4 = __riscv_vadd_vx_u32m4(t3, 0x10000, vl);
+
+ const vuint32m4_t result = __riscv_vmerge_vvm_u32m4(utf32, t4, surhi, vl);
+
+ const vuint32m4_t comp = __riscv_vcompress_vm_u32m4(result, compress, vl);
+ const size_t vlOut = __riscv_vcpop_m_b8(compress, vl);
+ __riscv_vse32_v_u32m4((uint32_t*)dst, comp, vlOut);
+
+ len -= vl;
+ src += vl;
+ dst += vlOut;
+
+ if ((last & LO_SURROGATE_MASK) == LO_SURROGATE_VALUE) {
+ // last item is lo surrogate and got already consumed
+ len -= 1;
+ src += 1;
+ }
+ }
+
+ return result(error_code::SUCCESS, dst - dstBeg);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ result res = convert_utf16le_to_utf32_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ result res = convert_utf16be_to_utf32_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ return rvv_utf16_to_utf32_with_errors<simdutf_ByteFlip::NONE>(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_utf16_to_utf32_with_errors<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_utf16_to_utf32_with_errors<simdutf_ByteFlip::V>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ return convert_utf16le_to_utf32(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t *src, size_t len, char32_t *dst) const noexcept {
+ return convert_utf16be_to_utf32(src, len, dst);
+}
+/* end file src/rvv/rvv_utf16_to.inl.cpp */
+/* begin file src/rvv/rvv_utf32_to.inl.cpp */
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf32_to_latin1_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t *src, size_t len, char *dst) const noexcept {
+ const char32_t *const beg = src;
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e32m8(len);
+ vuint32m8_t v = __riscv_vle32_v_u32m8((uint32_t*)src, vl);
+ long idx = __riscv_vfirst_m_b4(__riscv_vmsgtu_vx_u32m8_b4(v, 255, vl), vl);
+ if (idx >= 0)
+ return result(error_code::TOO_LARGE, src - beg + idx);
+ /* We don't use vcompress here, because its performance varies widely on current platforms.
+ * This might be worth reconsidering once there is more hardware available. */
+ __riscv_vse8_v_u8m2((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m2(__riscv_vncvt_x_x_w_u16m4(v, vl), vl), vl);
+ }
+ return result(error_code::SUCCESS, src - beg);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t *src, size_t len, char *dst) const noexcept {
+ return convert_utf32_to_latin1(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t *src, size_t len, char *dst) const noexcept {
+ size_t n = len;
+ const char32_t *srcBeg = src;
+ const char *dstBeg = dst;
+ size_t vl8m4 = __riscv_vsetvlmax_e8m4();
+ vbool2_t m4mulp2 = __riscv_vmseq_vx_u8m4_b2(__riscv_vand_vx_u8m4(__riscv_vid_v_u8m4(vl8m4), 3, vl8m4), 2, vl8m4);
+
+ for (size_t vl, vlOut; n > 0; ) {
+ vl = __riscv_vsetvl_e32m4(n);
+
+ vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t const*)src, vl);
+ vbool8_t m234 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x80-1, vl);
+ vuint16m2_t vn = __riscv_vncvt_x_x_w_u16m2(v, vl);
+
+ if (__riscv_vfirst_m_b8(m234, vl) < 0) { /* 1 byte utf8 */
+ vlOut = vl;
+ __riscv_vse8_v_u8m1((uint8_t*)dst, __riscv_vncvt_x_x_w_u8m1(vn, vlOut), vlOut);
+ n -= vl, src += vl, dst += vlOut;
+ continue;
+ }
+
+ vbool8_t m34 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x800-1, vl);
+
+ if (__riscv_vfirst_m_b8(m34,vl) < 0) { /* 1/2 byte utf8 */
+ /* 0: [ aaa|aabbbbbb]
+ * 1: [aabbbbbb| ] vsll 8
+ * 2: [ | aaaaa] vsrl 6
+ * 3: [00111111|00111111]
+ * 4: [ bbbbbb|000aaaaa] (1|2)&3
+ * 5: [10000000|11000000]
+ * 6: [10bbbbbb|110aaaaa] 4|5 */
+ vuint16m2_t twoByte =
+ __riscv_vand_vx_u16m2(__riscv_vor_vv_u16m2(
+ __riscv_vsll_vx_u16m2(vn, 8, vl),
+ __riscv_vsrl_vx_u16m2(vn, 6, vl),
+ vl), 0b0011111100111111, vl);
+ vuint16m2_t vout16 = __riscv_vor_vx_u16m2_mu(m234, vn, twoByte, 0b1000000011000000, vl);
+ vuint8m2_t vout = __riscv_vreinterpret_v_u16m2_u8m2(vout16);
+
+ /* Every high byte that is zero should be compressed
+ * low bytes should never be compressed, so we set them
+ * to all ones, and then create a non-zero bytes mask */
+ vbool4_t mcomp = __riscv_vmsne_vx_u8m2_b4(__riscv_vreinterpret_v_u16m2_u8m2(__riscv_vor_vx_u16m2(vout16, 0xFF, vl)), 0, vl*2);
+ vlOut = __riscv_vcpop_m_b4(mcomp, vl*2);
+
+ vout = __riscv_vcompress_vm_u8m2(vout, mcomp, vl*2);
+ __riscv_vse8_v_u8m2((uint8_t*)dst, vout, vlOut);
+
+ n -= vl, src += vl, dst += vlOut;
+ continue;
+ }
+
+ vbool8_t sur = __riscv_vmseq_vx_u32m4_b8(__riscv_vand_vx_u32m4(v, 0xFFFFF800, vl), 0xD800, vl);
+ long idx = __riscv_vfirst_m_b8(sur, vl);
+ if (idx >= 0) return result(error_code::SURROGATE, src - srcBeg + idx);
+
+ vbool8_t m4 = __riscv_vmsgtu_vx_u32m4_b8(v, 0x10000-1, vl);
+ long first = __riscv_vfirst_m_b8(m4, vl);
+ size_t tail = vl - first;
+ vl = first < 0 ? vl : first;
+
+ if (vl > 0) { /* 1/2/3 byte utf8 */
+ /* vn: [aaaabbbb|bbcccccc]
+ * v1: [0bcccccc| ] vsll 8
+ * v1: [10cccccc| ] vsll 8 & 0b00111111 | 0b10000000
+ * v2: [ |110bbbbb] vsrl 6 & 0b00111111 | 0b11000000
+ * v2: [ |10bbbbbb] vsrl 6 & 0b00111111 | 0b10000000
+ * v3: [ |1110aaaa] vsrl 12 | 0b11100000
+ * 1: [00000000|0bcccccc|00000000|00000000] => [0bcccccc]
+ * 2: [00000000|10cccccc|110bbbbb|00000000] => [110bbbbb] [10cccccc]
+ * 3: [00000000|10cccccc|10bbbbbb|1110aaaa] => [1110aaaa] [10bbbbbb] [10cccccc]
+ */
+ vuint16m2_t v1, v2, v3, v12;
+ v1 = __riscv_vor_vx_u16m2_mu(m234, vn, __riscv_vand_vx_u16m2(vn, 0b00111111, vl), 0b10000000, vl);
+ v1 = __riscv_vsll_vx_u16m2(v1, 8, vl);
+
+ v2 = __riscv_vor_vx_u16m2(__riscv_vand_vx_u16m2(__riscv_vsrl_vx_u16m2(vn, 6, vl), 0b00111111, vl), 0b10000000, vl);
+ v2 = __riscv_vor_vx_u16m2_mu(__riscv_vmnot_m_b8(m34,vl), v2, v2, 0b01000000, vl);
+ v3 = __riscv_vor_vx_u16m2(__riscv_vsrl_vx_u16m2(vn, 12, vl), 0b11100000, vl);
+ v12 = __riscv_vor_vv_u16m2_mu(m234, v1, v1, v2, vl);
+
+ vuint32m4_t w12 = __riscv_vwmulu_vx_u32m4(v12, 1<<8, vl);
+ vuint32m4_t w123 = __riscv_vwaddu_wv_u32m4_mu(m34, w12, w12, v3, vl);
+ vuint8m4_t vout = __riscv_vreinterpret_v_u32m4_u8m4(w123);
+
+ vbool2_t mcomp = __riscv_vmor_mm_b2(m4mulp2, __riscv_vmsne_vx_u8m4_b2(vout, 0, vl*4), vl*4);
+ vlOut = __riscv_vcpop_m_b2(mcomp, vl*4);
+
+ vout = __riscv_vcompress_vm_u8m4(vout, mcomp, vl*4);
+ __riscv_vse8_v_u8m4((uint8_t*)dst, vout, vlOut);
+
+ n -= vl, src += vl, dst += vlOut;
+ }
+
+ if (tail) while (n) {
+ uint32_t word = src[0];
+ if (word < 0x10000) break;
+ if (word > 0x10FFFF) return result(error_code::TOO_LARGE, src - srcBeg);
+ *dst++ = (uint8_t)(( word>>18) | 0b11110000);
+ *dst++ = (uint8_t)(((word>>12) & 0b111111) | 0b10000000);
+ *dst++ = (uint8_t)(((word>> 6) & 0b111111) | 0b10000000);
+ *dst++ = (uint8_t)(( word & 0b111111) | 0b10000000);
+ ++src;
+ --n;
+ }
+ }
+
+ return result(error_code::SUCCESS, dst - dstBeg);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t *src, size_t len, char *dst) const noexcept {
+ result res = convert_utf32_to_utf8_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t *src, size_t len, char *dst) const noexcept {
+ return convert_utf32_to_utf8(src, len, dst);
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static result rvv_convert_utf32_to_utf16_with_errors(const char32_t *src, size_t len, char16_t *dst) {
+ size_t vl8m2 = __riscv_vsetvlmax_e8m2();
+ vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2);
+ const char16_t *dstBeg = dst;
+ const char32_t *srcBeg = src;
+ for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) {
+ vl = __riscv_vsetvl_e32m4(len);
+ vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t*)src, vl);
+ vuint32m4_t off = __riscv_vadd_vx_u32m4(v, 0xFFFF2000, vl);
+ long idx;
+ idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(off, 0xFFFFF7FF, vl), vl);
+ if (idx >= 0) return result(error_code::SURROGATE, src - srcBeg + idx);
+ idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0xFFFF, vl), vl);
+ if (idx < 0) {
+ vlOut = vl;
+ vuint16m2_t n = simdutf_byteflip<bflip>(__riscv_vncvt_x_x_w_u16m2(v, vlOut), vlOut);
+ __riscv_vse16_v_u16m2((uint16_t*)dst, n, vlOut);
+ continue;
+ }
+ idx = __riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0x10FFFF, vl), vl);
+ if (idx >= 0) return result(error_code::TOO_LARGE, src - srcBeg + idx);
+ vlOut = rvv_utf32_store_utf16_m4<bflip>((uint16_t*)dst, v, vl, m4even);
+ }
+ return result(error_code::SUCCESS, dst - dstBeg);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ result res = convert_utf32_to_utf16le_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ result res = convert_utf32_to_utf16be_with_errors(src, len, dst);
+ return res.error == error_code::SUCCESS ? res.count : 0;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ return rvv_convert_utf32_to_utf16_with_errors<simdutf_ByteFlip::NONE>(src, len, dst);
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_convert_utf32_to_utf16_with_errors<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_convert_utf32_to_utf16_with_errors<simdutf_ByteFlip::V>(src, len, dst);
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static size_t rvv_convert_valid_utf32_to_utf16(const char32_t *src, size_t len, char16_t *dst) {
+ size_t vl8m2 = __riscv_vsetvlmax_e8m2();
+ vbool4_t m4even = __riscv_vmseq_vx_u8m2_b4(__riscv_vand_vx_u8m2(__riscv_vid_v_u8m2(vl8m2), 1, vl8m2), 0, vl8m2);
+ char16_t *dstBeg = dst;
+ for (size_t vl, vlOut; len > 0; len -= vl, src += vl, dst += vlOut) {
+ vl = __riscv_vsetvl_e32m4(len);
+ vuint32m4_t v = __riscv_vle32_v_u32m4((uint32_t*)src, vl);
+ if (__riscv_vfirst_m_b8(__riscv_vmsgtu_vx_u32m4_b8(v, 0xFFFF, vl), vl) < 0) {
+ vlOut = vl;
+ vuint16m2_t n = simdutf_byteflip<bflip>(__riscv_vncvt_x_x_w_u16m2(v, vlOut), vlOut);
+ __riscv_vse16_v_u16m2((uint16_t*)dst, n, vlOut);
+ continue;
+ }
+ vlOut = rvv_utf32_store_utf16_m4<bflip>((uint16_t*)dst, v, vl, m4even);
+ }
+ return dst - dstBeg;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ return rvv_convert_valid_utf32_to_utf16<simdutf_ByteFlip::NONE>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t *src, size_t len, char16_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_convert_valid_utf32_to_utf16<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_convert_valid_utf32_to_utf16<simdutf_ByteFlip::V>(src, len, dst);
+}
+/* end file src/rvv/rvv_utf32_to.inl.cpp */
+
+simdutf_warn_unused int implementation::detect_encodings(const char *input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if (bom_encoding != encoding_type::unspecified)
+ return bom_encoding;
+ int out = 0;
+ if (validate_utf8(input, length))
+ out |= encoding_type::UTF8;
+ if (length % 2 == 0) {
+ if (validate_utf16(reinterpret_cast<const char16_t*>(input), length/2))
+ out |= encoding_type::UTF16_LE;
+ }
+ if (length % 4 == 0) {
+ if (validate_utf32(reinterpret_cast<const char32_t*>(input), length/4))
+ out |= encoding_type::UTF32_LE;
+ }
+
+ return out;
+}
+
+template<simdutf_ByteFlip bflip>
+simdutf_really_inline static void rvv_change_endianness_utf16(const char16_t *src, size_t len, char16_t *dst) {
+ for (size_t vl; len > 0; len -= vl, src += vl, dst += vl) {
+ vl = __riscv_vsetvl_e16m8(len);
+ vuint16m8_t v = __riscv_vle16_v_u16m8((uint16_t*)src, vl);
+ __riscv_vse16_v_u16m8((uint16_t *)dst, simdutf_byteflip<bflip>(v, vl), vl);
+ }
+}
+
+void implementation::change_endianness_utf16(const char16_t *src, size_t len, char16_t *dst) const noexcept {
+ if (supports_zvbb())
+ return rvv_change_endianness_utf16<simdutf_ByteFlip::ZVBB>(src, len, dst);
+ else
+ return rvv_change_endianness_utf16<simdutf_ByteFlip::V>(src, len, dst);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ size_t equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ size_t equallocation = length; // location of the first padding character if any
+ auto equalsigns = 0;
+ if(length > 0 && input[length - 1] == '=') {
+ length -= 1;
+ equalsigns++;
+ while(length > 0 && scalar::base64::is_ascii_white_space(input[length - 1])) {
+ length--;
+ }
+ if(length > 0 && input[length - 1] == '=') {
+ equalsigns++;
+ length -= 1;
+ }
+ }
+ if(length == 0) {
+ if(equalsigns > 0) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ return {SUCCESS, 0};
+ }
+ result r = scalar::base64::base64_tail_decode(output, input, length, options);
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, equallocation};
+ }
+ }
+ return r;
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return scalar::base64::tail_encode_base64(output, input, length, options);
+}
+} // namespace rvv
+} // namespace simdutf
+
+/* begin file src/simdutf/rvv/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_RVV
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+/* end file src/simdutf/rvv/end.h */
+/* end file src/rvv/implementation.cpp */
+#endif
+#if SIMDUTF_IMPLEMENTATION_WESTMERE
+/* begin file src/westmere/implementation.cpp */
+/* begin file src/simdutf/westmere/begin.h */
+// redefining SIMDUTF_IMPLEMENTATION to "westmere"
+// #define SIMDUTF_IMPLEMENTATION westmere
+
+#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE
+// nothing needed.
+#else
+SIMDUTF_TARGET_WESTMERE
+#endif
+/* end file src/simdutf/westmere/begin.h */
+namespace simdutf {
+namespace westmere {
+namespace {
+#ifndef SIMDUTF_WESTMERE_H
+#error "westmere.h must be included"
+#endif
+using namespace simd;
+
+simdutf_really_inline bool is_ascii(const simd8x64<uint8_t>& input) {
+ return input.reduce_or().is_ascii();
+}
+
+simdutf_unused simdutf_really_inline simd8<bool> must_be_continuation(const simd8<uint8_t> prev1, const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_second_byte = prev1.saturating_sub(0b11000000u-1); // Only 11______ will be > 0
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0b11100000u-1); // Only 111_____ will be > 0
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0b11110000u-1); // Only 1111____ will be > 0
+ // Caller requires a bool (all 1's). All values resulting from the subtraction will be <= 64, so signed comparison is fine.
+ return simd8<int8_t>(is_second_byte | is_third_byte | is_fourth_byte) > int8_t(0);
+}
+
+simdutf_really_inline simd8<bool> must_be_2_3_continuation(const simd8<uint8_t> prev2, const simd8<uint8_t> prev3) {
+ simd8<uint8_t> is_third_byte = prev2.saturating_sub(0xe0u-0x80); // Only 111_____ will be >= 0x80
+ simd8<uint8_t> is_fourth_byte = prev3.saturating_sub(0xf0u-0x80); // Only 1111____ will be >= 0x80
+ return simd8<bool>(is_third_byte | is_fourth_byte);
+}
+
+/* begin file src/westmere/internal/loader.cpp */
+namespace internal {
+namespace westmere {
+
+/* begin file src/westmere/internal/write_v_u16_11bits_to_utf8.cpp */
+/*
+* reads a vector of uint16 values
+* bits after 11th are ignored
+* first 11 bits are encoded into utf8
+* !important! utf8_output must have at least 16 writable bytes
+*/
+
+inline void write_v_u16_11bits_to_utf8(
+ const __m128i v_u16,
+ char*& utf8_output,
+ const __m128i one_byte_bytemask,
+ const uint16_t one_byte_bitmask
+) {
+ // 0b1100_0000_1000_0000
+ const __m128i v_c080 = _mm_set1_epi16((int16_t)0xc080);
+ // 0b0001_1111_0000_0000
+ const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
+ // 0b0000_0000_0011_1111
+ const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f);
+
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m128i t0 = _mm_slli_epi16(v_u16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m128i t1 = _mm_and_si128(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m128i t2 = _mm_and_si128(v_u16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m128i t3 = _mm_or_si128(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m128i t4 = _mm_or_si128(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m128i utf8_unpacked = _mm_blendv_epi8(t4, v_u16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB)
+ const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
+ const uint16_t m1 = static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
+ const uint8_t m2 = static_cast<uint8_t>((m0 | m1) & 0xff); // m2 = hdgcfbea
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ utf8_output += row[0];
+}
+
+inline void write_v_u16_11bits_to_utf8(
+ const __m128i v_u16,
+ char*& utf8_output,
+ const __m128i v_0000,
+ const __m128i v_ff80
+) {
+ // no bits set above 7th bit
+ const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(v_u16, v_ff80), v_0000);
+ const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
+
+ write_v_u16_11bits_to_utf8(
+ v_u16, utf8_output, one_byte_bytemask, one_byte_bitmask);
+}
+/* end file src/westmere/internal/write_v_u16_11bits_to_utf8.cpp */
+
+} // namespace westmere
+} // namespace internal
+/* end file src/westmere/internal/loader.cpp */
+/* begin file src/westmere/sse_detect_encodings.cpp */
+template<class checker>
+// len is known to be a multiple of 2 when this is called
+int sse_detect_encodings(const char * buf, size_t len) {
+ const char* start = buf;
+ const char* end = buf + len;
+
+ bool is_utf8 = true;
+ bool is_utf16 = true;
+ bool is_utf32 = true;
+
+ int out = 0;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+
+ __m128i currentmax = _mm_setzero_si128();
+
+ checker check{};
+
+ while(buf + 64 <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ __m128i secondin = _mm_loadu_si128((__m128i*)buf+1);
+ __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2);
+ __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3);
+
+ const auto u0 = simd16<uint16_t>(in);
+ const auto u1 = simd16<uint16_t>(secondin);
+ const auto u2 = simd16<uint16_t>(thirdin);
+ const auto u3 = simd16<uint16_t>(fourthin);
+
+ const auto v0 = u0.shr<8>();
+ const auto v1 = u1.shr<8>();
+ const auto v2 = u2.shr<8>();
+ const auto v3 = u3.shr<8>();
+
+ const auto in16 = simd16<uint16_t>::pack(v0, v1);
+ const auto nextin16 = simd16<uint16_t>::pack(v2, v3);
+
+ const auto surrogates_wordmask0 = (in16 & v_f8) == v_d8;
+ const auto surrogates_wordmask1 = (nextin16 & v_f8) == v_d8;
+ uint16_t surrogates_bitmask0 = static_cast<uint16_t>(surrogates_wordmask0.to_bitmask());
+ uint16_t surrogates_bitmask1 = static_cast<uint16_t>(surrogates_wordmask1.to_bitmask());
+
+ // Check for surrogates
+ if (surrogates_bitmask0 != 0x0 || surrogates_bitmask1 != 0x0) {
+ // Cannot be UTF8
+ is_utf8 = false;
+ // Can still be either UTF-16LE or UTF-32 depending on the positions of the surrogates
+ // To be valid UTF-32, a surrogate cannot be in the two most significant bytes of any 32-bit word.
+ // On the other hand, to be valid UTF-16LE, at least one surrogate must be in the two most significant
+ // bytes of a 32-bit word since they always come in pairs in UTF-16LE.
+ // Note that we always proceed in multiple of 4 before this point so there is no offset in 32-bit code units.
+
+ if (((surrogates_bitmask0 | surrogates_bitmask1) & 0xaaaa) != 0) {
+ is_utf32 = false;
+ // Code from sse_validate_utf16le.cpp
+ // Not efficient, we do not process surrogates_bitmask1
+ const char16_t * input = reinterpret_cast<const char16_t*>(buf);
+ const char16_t* end16 = reinterpret_cast<const char16_t*>(start) + len/2;
+
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ const uint16_t V0 = static_cast<uint16_t>(~surrogates_bitmask0);
+
+ const auto vH0 = (in16 & v_fc) == v_dc;
+ const uint16_t H0 = static_cast<uint16_t>(vH0.to_bitmask());
+
+ const uint16_t L0 = static_cast<uint16_t>(~H0 & surrogates_bitmask0);
+
+ const uint16_t a0 = static_cast<uint16_t>(L0 & (H0 >> 1));
+
+ const uint16_t b0 = static_cast<uint16_t>(a0 << 1);
+
+ const uint16_t c0 = static_cast<uint16_t>(V0 | a0 | b0);
+
+ if (c0 == 0xffff) {
+ input += 16;
+ } else if (c0 == 0x7fff) {
+ input += 15;
+ } else {
+ is_utf16 = false;
+ break;
+ }
+
+ while (input + simd16<uint16_t>::SIZE * 2 < end16) {
+ const auto in0 = simd16<uint16_t>(input);
+ const auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in_16 = simd16<uint16_t>::pack(t0, t1);
+
+ const auto surrogates_wordmask = (in_16 & v_f8) == v_d8;
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
+ if (surrogates_bitmask == 0x0) {
+ input += 16;
+ } else {
+ const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
+
+ const auto vH = (in_16 & v_fc) == v_dc;
+ const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
+
+ const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
+
+ const uint16_t a = static_cast<uint16_t>(L & (H >> 1));
+
+ const uint16_t b = static_cast<uint16_t>(a << 1);
+
+ const uint16_t c = static_cast<uint16_t>(V | a | b);
+
+ if (c == 0xffff) {
+ input += 16;
+ } else if (c == 0x7fff) {
+ input += 15;
+ } else {
+ is_utf16 = false;
+ break;
+ }
+ }
+ }
+ } else {
+ is_utf16 = false;
+ // Check for UTF-32
+ if (len % 4 == 0) {
+ const char32_t * input = reinterpret_cast<const char32_t*>(buf);
+ const char32_t* end32 = reinterpret_cast<const char32_t*>(start) + len/4;
+
+ // Must start checking for surrogates
+ __m128i currentoffsetmax = _mm_setzero_si128();
+ const __m128i offset = _mm_set1_epi32(0xffff2000);
+ const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff);
+
+ currentmax = _mm_max_epu32(in, currentmax);
+ currentmax = _mm_max_epu32(secondin, currentmax);
+ currentmax = _mm_max_epu32(thirdin, currentmax);
+ currentmax = _mm_max_epu32(fourthin, currentmax);
+
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(secondin, offset), currentoffsetmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(thirdin, offset), currentoffsetmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(fourthin, offset), currentoffsetmax);
+
+ while (input + 4 < end32) {
+ const __m128i in32 = _mm_loadu_si128((__m128i *)input);
+ currentmax = _mm_max_epu32(in32,currentmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in32, offset), currentoffsetmax);
+ input += 4;
+ }
+
+ __m128i forbidden_words = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm_testz_si128(forbidden_words, forbidden_words) == 0) {
+ is_utf32 = false;
+ }
+ } else {
+ is_utf32 = false;
+ }
+ }
+ break;
+ }
+ // If no surrogate, validate under other encodings as well
+
+ // UTF-32 validation
+ currentmax = _mm_max_epu32(in, currentmax);
+ currentmax = _mm_max_epu32(secondin, currentmax);
+ currentmax = _mm_max_epu32(thirdin, currentmax);
+ currentmax = _mm_max_epu32(fourthin, currentmax);
+
+ // UTF-8 validation
+ // Relies on ../generic/utf8_validation/utf8_lookup4_algorithm.h
+ simd::simd8x64<uint8_t> in8(in, secondin, thirdin, fourthin);
+ check.check_next_input(in8);
+
+ buf += 64;
+ }
+
+ // Check which encodings are possible
+
+ if (is_utf8) {
+ if (static_cast<size_t>(buf - start) != len) {
+ uint8_t block[64]{};
+ std::memset(block, 0x20, 64);
+ std::memcpy(block, buf, len - (buf - start));
+ simd::simd8x64<uint8_t> in(block);
+ check.check_next_input(in);
+ }
+ if (!check.errors()) {
+ out |= simdutf::encoding_type::UTF8;
+ }
+ }
+
+ if (is_utf16 && scalar::utf16::validate<endianness::LITTLE>(reinterpret_cast<const char16_t*>(buf), (len - (buf - start))/2)) {
+ out |= simdutf::encoding_type::UTF16_LE;
+ }
+
+ if (is_utf32 && (len % 4 == 0)) {
+ const __m128i standardmax = _mm_set1_epi32(0x10ffff);
+ __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax);
+ if (_mm_testz_si128(is_zero, is_zero) == 1 && scalar::utf32::validate(reinterpret_cast<const char32_t*>(buf), (len - (buf - start))/4)) {
+ out |= simdutf::encoding_type::UTF32_LE;
+ }
+ }
+
+ return out;
+}
+/* end file src/westmere/sse_detect_encodings.cpp */
+
+/* begin file src/westmere/sse_validate_utf16.cpp */
+/*
+ In UTF-16 code units in range 0xD800 to 0xDFFF have special meaning.
+
+ In a vectorized algorithm we want to examine the most significant
+ nibble in order to select a fast path. If none of highest nibbles
+ are 0xD (13), than we are sure that UTF-16 chunk in a vector
+ register is valid.
+
+ Let us analyze what we need to check if the nibble is 0xD. The
+ value of the preceding nibble determines what we have:
+
+ 0xd000 .. 0xd7ff - a valid word
+ 0xd800 .. 0xdbff - low surrogate
+ 0xdc00 .. 0xdfff - high surrogate
+
+ Other constraints we have to consider:
+ - there must not be two consecutive low surrogates (0xd800 .. 0xdbff)
+ - there must not be two consecutive high surrogates (0xdc00 .. 0xdfff)
+ - there must not be sole low surrogate nor high surrogate
+
+ We're going to build three bitmasks based on the 3rd nibble:
+ - V = valid word,
+ - L = low surrogate (0xd800 .. 0xdbff)
+ - H = high surrogate (0xdc00 .. 0xdfff)
+
+ 0 1 2 3 4 5 6 7 <--- word index
+ [ V | L | H | L | H | V | V | L ]
+ 1 0 0 0 0 1 1 0 - V = valid masks
+ 0 1 0 1 0 0 0 1 - L = low surrogate
+ 0 0 1 0 1 0 0 0 - H high surrogate
+
+
+ 1 0 0 0 0 1 1 0 V = valid masks
+ 0 1 0 1 0 0 0 0 a = L & (H >> 1)
+ 0 0 1 0 1 0 0 0 b = a << 1
+ 1 1 1 1 1 1 1 0 c = V | a | b
+ ^
+ the last bit can be zero, we just consume 7 code units
+ and recheck this word in the next iteration
+*/
+
+/* Returns:
+ - pointer to the last unprocessed character (a scalar fallback should check the rest);
+ - nullptr if an error was detected.
+*/
+template <endianness big_endian>
+const char16_t* sse_validate_utf16(const char16_t* input, size_t size) {
+ const char16_t* end = input + size;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ while (input + simd16<uint16_t>::SIZE * 2 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+ if (big_endian) {
+ in0 = in0.swap_bytes();
+ in1 = in1.swap_bytes();
+ }
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in = simd16<uint16_t>::pack(t0, t1);
+
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const auto surrogates_wordmask = (in & v_f8) == v_d8;
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
+ if (surrogates_bitmask == 0x0000) {
+ input += 16;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = (in & v_fc) == v_dc;
+ const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
+
+ const uint16_t a = static_cast<uint16_t>(L & (H >> 1)); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint16_t b = static_cast<uint16_t>(a << 1); // Just mark that the opinput - startite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint16_t c = static_cast<uint16_t>(V | a | b); // Combine all the masks into the final one.
+
+ if (c == 0xffff) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += 16;
+ } else if (c == 0x7fff) {
+ // The 15 lower code units of the input register contains valid UTF-16.
+ // The 15th word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += 15;
+ } else {
+ return nullptr;
+ }
+ }
+ }
+
+ return input;
+}
+
+
+template <endianness big_endian>
+const result sse_validate_utf16_with_errors(const char16_t* input, size_t size) {
+ const char16_t* start = input;
+ const char16_t* end = input + size;
+
+ const auto v_d8 = simd8<uint8_t>::splat(0xd8);
+ const auto v_f8 = simd8<uint8_t>::splat(0xf8);
+ const auto v_fc = simd8<uint8_t>::splat(0xfc);
+ const auto v_dc = simd8<uint8_t>::splat(0xdc);
+
+ while (input + simd16<uint16_t>::SIZE * 2 < end) {
+ // 0. Load data: since the validation takes into account only higher
+ // byte of each word, we compress the two vectors into one which
+ // consists only the higher bytes.
+ auto in0 = simd16<uint16_t>(input);
+ auto in1 = simd16<uint16_t>(input + simd16<uint16_t>::SIZE / sizeof(char16_t));
+
+ if (big_endian) {
+ in0 = in0.swap_bytes();
+ in1 = in1.swap_bytes();
+ }
+
+ const auto t0 = in0.shr<8>();
+ const auto t1 = in1.shr<8>();
+
+ const auto in = simd16<uint16_t>::pack(t0, t1);
+
+ // 1. Check whether we have any 0xD800..DFFF word (0b1101'1xxx'yyyy'yyyy).
+ const auto surrogates_wordmask = (in & v_f8) == v_d8;
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(surrogates_wordmask.to_bitmask());
+ if (surrogates_bitmask == 0x0000) {
+ input += 16;
+ } else {
+ // 2. We have some surrogates that have to be distinguished:
+ // - low surrogates: 0b1101'10xx'yyyy'yyyy (0xD800..0xDBFF)
+ // - high surrogates: 0b1101'11xx'yyyy'yyyy (0xDC00..0xDFFF)
+ //
+ // Fact: high surrogate has 11th bit set (3rd bit in the higher word)
+
+ // V - non-surrogate code units
+ // V = not surrogates_wordmask
+ const uint16_t V = static_cast<uint16_t>(~surrogates_bitmask);
+
+ // H - word-mask for high surrogates: the six highest bits are 0b1101'11
+ const auto vH = (in & v_fc) == v_dc;
+ const uint16_t H = static_cast<uint16_t>(vH.to_bitmask());
+
+ // L - word mask for low surrogates
+ // L = not H and surrogates_wordmask
+ const uint16_t L = static_cast<uint16_t>(~H & surrogates_bitmask);
+
+ const uint16_t a = static_cast<uint16_t>(L & (H >> 1)); // A low surrogate must be followed by high one.
+ // (A low surrogate placed in the 7th register's word
+ // is an exception we handle.)
+ const uint16_t b = static_cast<uint16_t>(a << 1); // Just mark that the opinput - startite fact is hold,
+ // thanks to that we have only two masks for valid case.
+ const uint16_t c = static_cast<uint16_t>(V | a | b); // Combine all the masks into the final one.
+
+ if (c == 0xffff) {
+ // The whole input register contains valid UTF-16, i.e.,
+ // either single code units or proper surrogate pairs.
+ input += 16;
+ } else if (c == 0x7fff) {
+ // The 15 lower code units of the input register contains valid UTF-16.
+ // The 15th word may be either a low or high surrogate. It the next
+ // iteration we 1) check if the low surrogate is followed by a high
+ // one, 2) reject sole high surrogate.
+ input += 15;
+ } else {
+ return result(error_code::SURROGATE, input - start);
+ }
+ }
+ }
+
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/westmere/sse_validate_utf16.cpp */
+/* begin file src/westmere/sse_validate_utf32le.cpp */
+/* Returns:
+ - pointer to the last unprocessed character (a scalar fallback should check the rest);
+ - nullptr if an error was detected.
+*/
+const char32_t* sse_validate_utf32le(const char32_t* input, size_t size) {
+ const char32_t* end = input + size;
+
+ const __m128i standardmax = _mm_set1_epi32(0x10ffff);
+ const __m128i offset = _mm_set1_epi32(0xffff2000);
+ const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff);
+ __m128i currentmax = _mm_setzero_si128();
+ __m128i currentoffsetmax = _mm_setzero_si128();
+
+ while (input + 4 < end) {
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ currentmax = _mm_max_epu32(in,currentmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax);
+ input += 4;
+ }
+ __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax);
+ if(_mm_test_all_zeros(is_zero, is_zero) == 0) {
+ return nullptr;
+ }
+
+ is_zero = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm_test_all_zeros(is_zero, is_zero) == 0) {
+ return nullptr;
+ }
+
+ return input;
+}
+
+
+const result sse_validate_utf32le_with_errors(const char32_t* input, size_t size) {
+ const char32_t* start = input;
+ const char32_t* end = input + size;
+
+ const __m128i standardmax = _mm_set1_epi32(0x10ffff);
+ const __m128i offset = _mm_set1_epi32(0xffff2000);
+ const __m128i standardoffsetmax = _mm_set1_epi32(0xfffff7ff);
+ __m128i currentmax = _mm_setzero_si128();
+ __m128i currentoffsetmax = _mm_setzero_si128();
+
+ while (input + 4 < end) {
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ currentmax = _mm_max_epu32(in,currentmax);
+ currentoffsetmax = _mm_max_epu32(_mm_add_epi32(in, offset), currentoffsetmax);
+
+ __m128i is_zero = _mm_xor_si128(_mm_max_epu32(currentmax, standardmax), standardmax);
+ if(_mm_test_all_zeros(is_zero, is_zero) == 0) {
+ return result(error_code::TOO_LARGE, input - start);
+ }
+
+ is_zero = _mm_xor_si128(_mm_max_epu32(currentoffsetmax, standardoffsetmax), standardoffsetmax);
+ if(_mm_test_all_zeros(is_zero, is_zero) == 0) {
+ return result(error_code::SURROGATE, input - start);
+ }
+ input += 4;
+ }
+
+ return result(error_code::SUCCESS, input - start);
+}
+/* end file src/westmere/sse_validate_utf32le.cpp */
+
+/* begin file src/westmere/sse_convert_latin1_to_utf8.cpp */
+std::pair<const char* const, char* const> sse_convert_latin1_to_utf8(
+ const char* latin_input,
+ const size_t latin_input_length,
+ char* utf8_output) {
+ const char* end = latin_input + latin_input_length;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ // 0b1000_0000
+ const __m128i v_80 = _mm_set1_epi8((uint8_t)0x80);
+ // 0b1111_1111_1000_0000
+ const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
+
+ const __m128i latin_1_half_into_u16_byte_mask = _mm_setr_epi8(
+ 0, '\x80',
+ 1, '\x80',
+ 2, '\x80',
+ 3, '\x80',
+ 4, '\x80',
+ 5, '\x80',
+ 6, '\x80',
+ 7, '\x80'
+ );
+
+ const __m128i latin_2_half_into_u16_byte_mask = _mm_setr_epi8(
+ 8, '\x80',
+ 9, '\x80',
+ 10, '\x80',
+ 11, '\x80',
+ 12, '\x80',
+ 13, '\x80',
+ 14, '\x80',
+ 15, '\x80'
+ );
+
+ // each latin1 takes 1-2 utf8 bytes
+ // slow path writes useful 8-15 bytes twice (eagerly writes 16 bytes and then adjust the pointer)
+ // so the last write can exceed the utf8_output size by 8-1 bytes
+ // by reserving 8 extra input bytes, we expect the output to have 8-16 bytes free
+ while (latin_input + 16 + 8 <= end) {
+ // Load 16 Latin1 characters (16 bytes) into a 128-bit register
+ __m128i v_latin = _mm_loadu_si128((__m128i*)latin_input);
+
+
+ if (_mm_testz_si128(v_latin, v_80)) {// ASCII fast path!!!!
+ _mm_storeu_si128((__m128i*)utf8_output, v_latin);
+ latin_input += 16;
+ utf8_output += 16;
+ continue;
+ }
+
+
+ // assuming a/b are bytes and A/B are uint16 of the same value
+ // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
+ __m128i v_u16_latin_1_half = _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
+ // aaaa_aaaa_bbbb_bbbb -> BBBB_BBBB
+ __m128i v_u16_latin_2_half = _mm_shuffle_epi8(v_latin, latin_2_half_into_u16_byte_mask);
+
+
+ internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half, utf8_output, v_0000, v_ff80);
+ internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_2_half, utf8_output, v_0000, v_ff80);
+ latin_input += 16;
+ }
+
+ if (latin_input + 16 <= end) {
+ // Load 16 Latin1 characters (16 bytes) into a 128-bit register
+ __m128i v_latin = _mm_loadu_si128((__m128i*)latin_input);
+
+ if (_mm_testz_si128(v_latin, v_80)) {// ASCII fast path!!!!
+ _mm_storeu_si128((__m128i*)utf8_output, v_latin);
+ latin_input += 16;
+ utf8_output += 16;
+ } else {
+ // assuming a/b are bytes and A/B are uint16 of the same value
+ // aaaa_aaaa_bbbb_bbbb -> AAAA_AAAA
+ __m128i v_u16_latin_1_half = _mm_shuffle_epi8(v_latin, latin_1_half_into_u16_byte_mask);
+ internal::westmere::write_v_u16_11bits_to_utf8(v_u16_latin_1_half, utf8_output, v_0000, v_ff80);
+ latin_input += 8;
+ }
+ }
+
+ return std::make_pair(latin_input, utf8_output);
+}
+/* end file src/westmere/sse_convert_latin1_to_utf8.cpp */
+/* begin file src/westmere/sse_convert_latin1_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char*, char16_t*> sse_convert_latin1_to_utf16(const char *latin1_input, size_t len,
+ char16_t *utf16_output) {
+ size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ // Load 16 Latin1 characters into a 128-bit register
+ __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i*>(&latin1_input[i]));
+ __m128i out1 = big_endian ? _mm_unpacklo_epi8(_mm_setzero_si128(), in)
+ : _mm_unpacklo_epi8(in, _mm_setzero_si128());
+ __m128i out2 = big_endian ? _mm_unpackhi_epi8(_mm_setzero_si128(), in)
+ : _mm_unpackhi_epi8(in, _mm_setzero_si128());
+ // Zero extend each Latin1 character to 16-bit integers and store the results back to memory
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(&utf16_output[i]), out1);
+ _mm_storeu_si128(reinterpret_cast<__m128i*>(&utf16_output[i + 8]), out2);
+ }
+ // return pointers pointing to where we left off
+ return std::make_pair(latin1_input + rounded_len, utf16_output + rounded_len);
+}
+/* end file src/westmere/sse_convert_latin1_to_utf16.cpp */
+/* begin file src/westmere/sse_convert_latin1_to_utf32.cpp */
+std::pair<const char*, char32_t*> sse_convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) {
+ const char* end = buf + len;
+
+ while (buf + 16 <= end) {
+ // Load 16 Latin1 characters (16 bytes) into a 128-bit register
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+
+ // Shift input to process next 4 bytes
+ __m128i in_shifted1 = _mm_srli_si128(in, 4);
+ __m128i in_shifted2 = _mm_srli_si128(in, 8);
+ __m128i in_shifted3 = _mm_srli_si128(in, 12);
+
+ // expand 8-bit to 32-bit unit
+ __m128i out1 = _mm_cvtepu8_epi32(in);
+ __m128i out2 = _mm_cvtepu8_epi32(in_shifted1);
+ __m128i out3 = _mm_cvtepu8_epi32(in_shifted2);
+ __m128i out4 = _mm_cvtepu8_epi32(in_shifted3);
+
+ _mm_storeu_si128((__m128i*)utf32_output, out1);
+ _mm_storeu_si128((__m128i*)(utf32_output + 4), out2);
+ _mm_storeu_si128((__m128i*)(utf32_output + 8), out3);
+ _mm_storeu_si128((__m128i*)(utf32_output + 12), out4);
+
+ utf32_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(buf, utf32_output);
+}
+
+/* end file src/westmere/sse_convert_latin1_to_utf32.cpp */
+
+
+/* begin file src/westmere/sse_convert_utf8_to_utf16.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+
+// Convert up to 12 bytes from utf8 to utf16 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+template <endianness big_endian>
+size_t convert_masked_utf8_to_utf16(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char16_t *&utf16_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ // We first try a few fast paths.
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ __m128i ascii_first = _mm_cvtepu8_epi16(in);
+ __m128i ascii_second = _mm_cvtepu8_epi16(_mm_srli_si128(in,8));
+ if (big_endian) {
+ ascii_first = _mm_shuffle_epi8(ascii_first, swap);
+ ascii_second = _mm_shuffle_epi8(ascii_second, swap);
+ }
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output), ascii_first);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf16_output + 8), ascii_second);
+ utf16_output += 16; // We wrote 16 16-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ if(((utf8_end_of_code_point_mask & 0xFFFF) == 0xaaaa)) {
+ // We want to take 8 2-byte UTF-8 code units and turn them into 8 2-byte UTF-16 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ if (big_endian) composed = _mm_shuffle_epi8(composed, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed);
+ utf16_output += 8; // We wrote 16 bytes, 8 code points.
+ return 16;
+ }
+ if(input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 2-byte UTF-16 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ __m128i composed_repacked = _mm_packus_epi32(composed, composed);
+ if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
+ utf16_output += 4;
+ return 12;
+ }
+ /// We do not have a fast path available, so we fallback.
+
+ const uint8_t idx =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ if (big_endian) composed = _mm_shuffle_epi8(composed, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed);
+ utf16_output += 6; // We wrote 12 bytes, 6 code points.
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ __m128i composed_repacked = _mm_packus_epi32(composed, composed);
+ if (big_endian) composed_repacked = _mm_shuffle_epi8(composed_repacked, swap);
+ _mm_storeu_si128((__m128i *)utf16_output, composed_repacked);
+ utf16_output += 4;
+ } else if (idx < 209) {
+ // TWO (2) input code-code units
+ //////////////
+ // There might be garbage inputs where a leading byte mascarades as a four-byte
+ // leading byte (by being followed by 3 continuation byte), but is not greater than
+ // 0xf0. This could trigger a buffer overflow if we only counted leading
+ // bytes of the form 0xf0 as generating surrogate pairs, without further UTF-8 validation.
+ // Thus we must be careful to ensure that only leading bytes at least as large as 0xf0 generate surrogate pairs.
+ // We do as at the cost of an extra mask.
+ /////////////
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
+ const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
+ // correct for spurious high bit
+ const __m128i correct =
+ _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
+ middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
+ const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
+ // We deliberately carry the leading four bits in highbyte if they are present,
+ // we remove them later when computing hightenbits.
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0xff000000));
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
+ // When we need to generate a surrogate pair (leading byte > 0xF0), then
+ // the corresponding 32-bit value in 'composed' will be greater than
+ // > (0xff00000>>6) or > 0x3c00000. This can be used later to identify the
+ // location of the surrogate pairs.
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
+ _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
+ const __m128i composedminus =
+ _mm_sub_epi32(composed, _mm_set1_epi32(0x10000));
+ const __m128i lowtenbits =
+ _mm_and_si128(composedminus, _mm_set1_epi32(0x3ff));
+ // Notice the 0x3ff mask:
+ const __m128i hightenbits = _mm_and_si128(_mm_srli_epi32(composedminus, 10), _mm_set1_epi32(0x3ff));
+ const __m128i lowtenbitsadd =
+ _mm_add_epi32(lowtenbits, _mm_set1_epi32(0xDC00));
+ const __m128i hightenbitsadd =
+ _mm_add_epi32(hightenbits, _mm_set1_epi32(0xD800));
+ const __m128i lowtenbitsaddshifted = _mm_slli_epi32(lowtenbitsadd, 16);
+ __m128i surrogates =
+ _mm_or_si128(hightenbitsadd, lowtenbitsaddshifted);
+ uint32_t basic_buffer[4];
+ uint32_t basic_buffer_swap[4];
+ if (big_endian) {
+ _mm_storeu_si128((__m128i *)basic_buffer_swap, _mm_shuffle_epi8(composed, swap));
+ surrogates = _mm_shuffle_epi8(surrogates, swap);
+ }
+ _mm_storeu_si128((__m128i *)basic_buffer, composed);
+ uint32_t surrogate_buffer[4];
+ _mm_storeu_si128((__m128i *)surrogate_buffer, surrogates);
+ for (size_t i = 0; i < 3; i++) {
+ if(basic_buffer[i] > 0x3c00000) {
+ utf16_output[0] = uint16_t(surrogate_buffer[i] & 0xffff);
+ utf16_output[1] = uint16_t(surrogate_buffer[i] >> 16);
+ utf16_output += 2;
+ } else {
+ utf16_output[0] = big_endian ? uint16_t(basic_buffer_swap[i]) : uint16_t(basic_buffer[i]);
+ utf16_output++;
+ }
+ }
+ } else {
+ // here we know that there is an error but we do not handle errors
+ }
+ return consumed;
+}
+/* end file src/westmere/sse_convert_utf8_to_utf16.cpp */
+/* begin file src/westmere/sse_convert_utf8_to_utf32.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+
+// Convert up to 12 bytes from utf8 to utf32 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+size_t convert_masked_utf8_to_utf32(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char32_t *&utf32_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ // We first try a few fast paths.
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff;
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu8_epi32(in));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu8_epi32(_mm_srli_si128(in,4)));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+8), _mm_cvtepu8_epi32(_mm_srli_si128(in,8)));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+12), _mm_cvtepu8_epi32(_mm_srli_si128(in,12)));
+ utf32_output += 16; // We wrote 16 32-bit characters.
+ return 16; // We consumed 16 bytes.
+ }
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xaaaa)) {
+ // We want to take 8 2-byte UTF-8 code units and turn them into 8 4-byte UTF-32 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(composed));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(composed,8)));
+ utf32_output += 8; // We wrote 32 bytes, 8 code points.
+ return 16;
+ }
+ if(input_utf8_end_of_code_point_mask == 0x924) {
+ // We want to take 4 3-byte UTF-8 code units and turn them into 4 4-byte UTF-32 code units.
+ // There is probably a more efficient sequence, but the following might do.
+ const __m128i sh = _mm_setr_epi8(2, 1, 0, -1, 5, 4, 3, -1, 8, 7, 6, -1, 11, 10, 9, -1);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 4;
+ return 12;
+ }
+ /// We do not have a fast path available, so we fallback.
+
+ const uint8_t idx =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ if (idx < 64) {
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ const __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(composed));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(composed,8)));
+ utf32_output += 6; // We wrote 12 bytes, 6 code points.
+ } else if (idx < 145) {
+ // FOUR (4) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii =
+ _mm_and_si128(perm, _mm_set1_epi32(0x7f)); // 7 or 6 bits
+ const __m128i middlebyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x3f00)); // 5 or 6 bits
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ const __m128i highbyte =
+ _mm_and_si128(perm, _mm_set1_epi32(0x0f0000)); // 4 bits
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 4);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted), highbyte_shifted);
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 4;
+ } else if (idx < 209) {
+ // TWO (2) input code-code units
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi32(0x7f));
+ const __m128i middlebyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f00));
+ const __m128i middlebyte_shifted = _mm_srli_epi32(middlebyte, 2);
+ __m128i middlehighbyte = _mm_and_si128(perm, _mm_set1_epi32(0x3f0000));
+ // correct for spurious high bit
+ const __m128i correct =
+ _mm_srli_epi32(_mm_and_si128(perm, _mm_set1_epi32(0x400000)), 1);
+ middlehighbyte = _mm_xor_si128(correct, middlehighbyte);
+ const __m128i middlehighbyte_shifted = _mm_srli_epi32(middlehighbyte, 4);
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi32(0x07000000));
+ const __m128i highbyte_shifted = _mm_srli_epi32(highbyte, 6);
+ const __m128i composed =
+ _mm_or_si128(_mm_or_si128(ascii, middlebyte_shifted),
+ _mm_or_si128(highbyte_shifted, middlehighbyte_shifted));
+ _mm_storeu_si128((__m128i *)utf32_output, composed);
+ utf32_output += 3;
+ } else {
+ // here we know that there is an error but we do not handle errors
+ }
+ return consumed;
+}
+/* end file src/westmere/sse_convert_utf8_to_utf32.cpp */
+/* begin file src/westmere/sse_convert_utf8_to_latin1.cpp */
+// depends on "tables/utf8_to_utf16_tables.h"
+
+
+// Convert up to 12 bytes from utf8 to latin1 using a mask indicating the
+// end of the code points. Only the least significant 12 bits of the mask
+// are accessed.
+// It returns how many bytes were consumed (up to 12).
+size_t convert_masked_utf8_to_latin1(const char *input,
+ uint64_t utf8_end_of_code_point_mask,
+ char *&latin1_output) {
+ // we use an approach where we try to process up to 12 input bytes.
+ // Why 12 input bytes and not 16? Because we are concerned with the size of
+ // the lookup tables. Also 12 is nicely divisible by two and three.
+ //
+ //
+ // Optimization note: our main path below is load-latency dependent. Thus it is maybe
+ // beneficial to have fast paths that depend on branch prediction but have less latency.
+ // This results in more instructions but, potentially, also higher speeds.
+ //
+ const __m128i in = _mm_loadu_si128((__m128i *)input);
+ const uint16_t input_utf8_end_of_code_point_mask =
+ utf8_end_of_code_point_mask & 0xfff; //we're only processing 12 bytes in case it`s not all ASCII
+ if(((utf8_end_of_code_point_mask & 0xffff) == 0xffff)) {
+ // We process the data in chunks of 16 bytes.
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(latin1_output), in);
+ latin1_output += 16; // We wrote 16 characters.
+ return 16; // We consumed 16 bytes.
+ }
+ /// We do not have a fast path available, so we fallback.
+ const uint8_t idx =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][0];
+ const uint8_t consumed =
+ tables::utf8_to_utf16::utf8bigindex[input_utf8_end_of_code_point_mask][1];
+ // this indicates an invalid input:
+ if(idx >= 64) { return consumed; }
+ // Here we should have (idx < 64), if not, there is a bug in the validation or elsewhere.
+ // SIX (6) input code-code units
+ // this is a relatively easy scenario
+ // we process SIX (6) input code-code units. The max length in bytes of six code
+ // code units spanning between 1 and 2 bytes each is 12 bytes. On processors
+ // where pdep/pext is fast, we might be able to use a small lookup table.
+ const __m128i sh =
+ _mm_loadu_si128((const __m128i *)tables::utf8_to_utf16::shufutf8[idx]);
+ const __m128i perm = _mm_shuffle_epi8(in, sh);
+ const __m128i ascii = _mm_and_si128(perm, _mm_set1_epi16(0x7f));
+ const __m128i highbyte = _mm_and_si128(perm, _mm_set1_epi16(0x1f00));
+ __m128i composed = _mm_or_si128(ascii, _mm_srli_epi16(highbyte, 2));
+ const __m128i latin1_packed = _mm_packus_epi16(composed,composed);
+ // writing 8 bytes even though we only care about the first 6 bytes.
+ // performance note: it would be faster to use _mm_storeu_si128, we should investigate.
+ _mm_storel_epi64((__m128i *)latin1_output, latin1_packed);
+ latin1_output += 6; // We wrote 6 bytes.
+ return consumed;
+}
+/* end file src/westmere/sse_convert_utf8_to_latin1.cpp */
+
+/* begin file src/westmere/sse_convert_utf16_to_latin1.cpp */
+template <endianness big_endian>
+std::pair<const char16_t*, char*> sse_convert_utf16_to_latin1(const char16_t* buf, size_t len, char* latin1_output) {
+ const char16_t* end = buf + len;
+ while (buf + 8 <= end) {
+ // Load 8 UTF-16 characters into 128-bit SSE register
+ __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i*>(buf));
+
+ if (!match_system(big_endian)) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+
+ __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
+ if (_mm_testz_si128(in, high_byte_mask)) {
+ // Pack 16-bit characters into 8-bit and store in latin1_output
+ __m128i latin1_packed = _mm_packus_epi16(in, in);
+ _mm_storel_epi64(reinterpret_cast<__m128i*>(latin1_output), latin1_packed);
+ // Adjust pointers for next iteration
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ return std::make_pair(nullptr, reinterpret_cast<char*>(latin1_output));
+ }
+ } // while
+ return std::make_pair(buf, latin1_output);
+}
+
+template <endianness big_endian>
+std::pair<result, char*> sse_convert_utf16_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+ while (buf + 8 <= end) {
+ __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i*>(buf));
+
+ if (!big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+
+ __m128i high_byte_mask = _mm_set1_epi16((int16_t)0xFF00);
+ if (_mm_testz_si128(in, high_byte_mask)) {
+ __m128i latin1_packed = _mm_packus_epi16(in, in);
+ _mm_storel_epi64(reinterpret_cast<__m128i*>(latin1_output), latin1_packed);
+ buf += 8;
+ latin1_output += 8;
+ } else {
+ // Fallback to scalar code for handling errors
+ for(int k = 0; k < 8; k++) {
+ uint16_t word = !match_system(big_endian) ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if(word <= 0xff) {
+ *latin1_output++ = char(word);
+ } else {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), latin1_output);
+ }
+ }
+ buf += 8;
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), latin1_output);
+}
+/* end file src/westmere/sse_convert_utf16_to_latin1.cpp */
+/* begin file src/westmere/sse_convert_utf16_to_utf8.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ is in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char*> sse_convert_utf16_to_utf8(const char16_t* buf, size_t len, char* utf8_output) {
+
+ const char16_t* end = buf + len;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+ // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
+ const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
+ if(_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ nextin = _mm_shuffle_epi8(nextin, swap);
+ }
+ if(!_mm_testz_si128(nextin, v_ff80)) {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in,in);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ in = nextin;
+ } else {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in,nextin);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ // no bits set above 7th bit
+ const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
+ const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
+ const uint16_t one_or_two_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
+
+ if (one_or_two_bytes_bitmask == 0xffff) {
+ internal::westmere::write_v_u16_11bits_to_utf8(in, utf8_output, one_byte_bytemask, one_byte_bitmask);
+ buf += 8;
+ continue;
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x0000) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m128i s0 = _mm_srli_epi16(in, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
+ const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m128i s4 = _mm_xor_si128(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
+ const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16_t mask = (one_byte_bitmask & 0x5555) |
+ (one_or_two_bytes_bitmask & 0xaaaa);
+ if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf8_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(buf, utf8_output);
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char*> sse_convert_utf16_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+ // a single 16-bit UTF-16 word can yield 1, 2 or 3 UTF-8 bytes
+ const __m128i v_ff80 = _mm_set1_epi16((int16_t)0xff80);
+ if(_mm_testz_si128(in, v_ff80)) { // ASCII fast path!!!!
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ nextin = _mm_shuffle_epi8(nextin, swap);
+ }
+ if(!_mm_testz_si128(nextin, v_ff80)) {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in,in);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ in = nextin;
+ } else {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in,nextin);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ // no bits set above 7th bit
+ const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_ff80), v_0000);
+ const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_0000);
+ const uint16_t one_or_two_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
+
+ if (one_or_two_bytes_bitmask == 0xffff) {
+ internal::westmere::write_v_u16_11bits_to_utf8(in, utf8_output, one_byte_bytemask, one_byte_bitmask);
+ buf += 8;
+ continue;
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x0000) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m128i t0 = _mm_shuffle_epi8(in, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m128i s0 = _mm_srli_epi16(in, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
+ const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m128i s4 = _mm_xor_si128(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
+ const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16_t mask = (one_byte_bitmask & 0x5555) |
+ (one_or_two_bytes_bitmask & 0xaaaa);
+ if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word & 0xFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xF800 ) != 0xD800) {
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf8_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf8_output++ = char((value>>18) | 0b11110000);
+ *utf8_output++ = char(((value>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((value>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((value & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
+}
+/* end file src/westmere/sse_convert_utf16_to_utf8.cpp */
+/* begin file src/westmere/sse_convert_utf16_to_utf32.cpp */
+/*
+ The vectorized algorithm works on single SSE register i.e., it
+ loads eight 16-bit code units.
+
+ We consider three cases:
+ 1. an input register contains no surrogates and each value
+ is in range 0x0000 .. 0x07ff.
+ 2. an input register contains no surrogates and values are
+ is in range 0x0000 .. 0xffff.
+ 3. an input register contains surrogates --- i.e. codepoints
+ can have 16 or 32 bits.
+
+ Ad 1.
+
+ When values are less than 0x0800, it means that a 16-bit code unit
+ can be converted into: 1) single UTF8 byte (when it's an ASCII
+ char) or 2) two UTF8 bytes.
+
+ For this case we do only some shuffle to obtain these 2-byte
+ codes and finally compress the whole SSE register with a single
+ shuffle.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+ Ad 2.
+
+ When values fit in 16-bit code units, but are above 0x07ff, then
+ a single word may produce one, two or three UTF8 bytes.
+
+ We prepare data for all these three cases in two registers.
+ The first register contains lower two UTF8 bytes (used in all
+ cases), while the second one contains just the third byte for
+ the three-UTF8-bytes case.
+
+ Finally these two registers are interleaved forming eight-element
+ array of 32-bit values. The array spans two SSE registers.
+ The bytes from the registers are compressed using two shuffles.
+
+ We need 256-entry lookup table to get a compression pattern
+ and the number of output bytes in the compressed vector register.
+ Each entry occupies 17 bytes.
+
+
+ To summarize:
+ - We need two 256-entry tables that have 8704 bytes in total.
+*/
+
+/*
+ Returns a pair: the first unprocessed byte from buf and utf8_output
+ A scalar routing should carry on the conversion of the tail.
+*/
+template <endianness big_endian>
+std::pair<const char16_t*, char32_t*> sse_convert_utf16_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const char16_t* end = buf + len;
+
+ const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
+
+ while (buf + 8 <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x0000) {
+ // case: no surrogate pair, extend 16-bit code units to 32-bit code units
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(in));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(in,8)));
+ utf32_output += 8;
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(nullptr, utf32_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(buf, utf32_output);
+}
+
+
+/*
+ Returns a pair: a result struct and utf8_output.
+ If there is an error, the count field of the result is the position of the error.
+ Otherwise, it is the position of the first unprocessed byte in buf (even if finished).
+ A scalar routing should carry on the conversion of the tail if needed.
+*/
+template <endianness big_endian>
+std::pair<result, char32_t*> sse_convert_utf16_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) {
+ const char16_t* start = buf;
+ const char16_t* end = buf + len;
+
+ const __m128i v_f800 = _mm_set1_epi16((int16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((int16_t)0xd800);
+
+ while (buf + 8 <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ in = _mm_shuffle_epi8(in, swap);
+ }
+
+ // 1. Check if there are any surrogate word in the input chunk.
+ // We have also deal with situation when there is a surrogate word
+ // at the end of a chunk.
+ const __m128i surrogates_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in, v_f800), v_d800);
+
+ // bitmask = 0x0000 if there are no surrogates
+ // = 0xc000 if the last word is a surrogate
+ const uint16_t surrogates_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(surrogates_bytemask));
+ // It might seem like checking for surrogates_bitmask == 0xc000 could help. However,
+ // it is likely an uncommon occurrence.
+ if (surrogates_bitmask == 0x0000) {
+ // case: no surrogate pair, extend 16-bit code units to 32-bit code units
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output), _mm_cvtepu16_epi32(in));
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(utf32_output+4), _mm_cvtepu16_epi32(_mm_srli_si128(in,8)));
+ utf32_output += 8;
+ buf += 8;
+ // surrogate pair(s) in a register
+ } else {
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint16_t word = big_endian ? scalar::utf16::swap_bytes(buf[k]) : buf[k];
+ if((word &0xF800 ) != 0xD800) {
+ *utf32_output++ = char32_t(word);
+ } else {
+ // must be a surrogate pair
+ uint16_t diff = uint16_t(word - 0xD800);
+ uint16_t next_word = big_endian ? scalar::utf16::swap_bytes(buf[k+1]) : buf[k+1];
+ k++;
+ uint16_t diff2 = uint16_t(next_word - 0xDC00);
+ if((diff | diff2) > 0x3FF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k - 1), utf32_output); }
+ uint32_t value = (diff << 10) + diff2 + 0x10000;
+ *utf32_output++ = char32_t(value);
+ }
+ }
+ buf += k;
+ }
+ } // while
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf32_output);
+}
+/* end file src/westmere/sse_convert_utf16_to_utf32.cpp */
+
+/* begin file src/westmere/sse_convert_utf32_to_latin1.cpp */
+std::pair<const char32_t *, char *>
+sse_convert_utf32_to_latin1(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
+
+ __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00);
+ __m128i shufmask =
+ _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
+
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ __m128i in1 = _mm_loadu_si128((__m128i *)buf);
+ __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4));
+ __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8));
+ __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12));
+
+ __m128i check_combined = _mm_or_si128(in1, in2);
+ check_combined = _mm_or_si128(check_combined, in3);
+ check_combined = _mm_or_si128(check_combined, in4);
+
+ if (!_mm_testz_si128(check_combined, high_bytes_mask)) {
+ return std::make_pair(nullptr, latin1_output);
+ }
+ __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask), _mm_shuffle_epi8(in2, shufmask));
+ __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask), _mm_shuffle_epi8(in4, shufmask));
+ __m128i pack = _mm_unpacklo_epi64(pack1, pack2);
+ _mm_storeu_si128((__m128i *)latin1_output, pack);
+ latin1_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(buf, latin1_output);
+}
+
+std::pair<result, char *>
+sse_convert_utf32_to_latin1_with_errors(const char32_t *buf, size_t len,
+ char *latin1_output) {
+ const char32_t *start = buf;
+ const size_t rounded_len = len & ~0xF; // Round down to nearest multiple of 16
+
+ __m128i high_bytes_mask = _mm_set1_epi32(0xFFFFFF00);
+ __m128i shufmask =
+ _mm_set_epi8(-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, 8, 4, 0);
+
+ for (size_t i = 0; i < rounded_len; i += 16) {
+ __m128i in1 = _mm_loadu_si128((__m128i *)buf);
+ __m128i in2 = _mm_loadu_si128((__m128i *)(buf + 4));
+ __m128i in3 = _mm_loadu_si128((__m128i *)(buf + 8));
+ __m128i in4 = _mm_loadu_si128((__m128i *)(buf + 12));
+
+ __m128i check_combined = _mm_or_si128(in1, in2);
+ check_combined = _mm_or_si128(check_combined, in3);
+ check_combined = _mm_or_si128(check_combined, in4);
+
+ if (!_mm_testz_si128(check_combined, high_bytes_mask)) {
+ // Fallback to scalar code for handling errors
+ for (int k = 0; k < 16; k++) {
+ char32_t codepoint = buf[k];
+ if (codepoint <= 0xff) {
+ *latin1_output++ = char(codepoint);
+ } else {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start + k),
+ latin1_output);
+ }
+ }
+ buf += 16;
+ continue;
+ }
+ __m128i pack1 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in1, shufmask), _mm_shuffle_epi8(in2, shufmask));
+ __m128i pack2 = _mm_unpacklo_epi32(_mm_shuffle_epi8(in3, shufmask), _mm_shuffle_epi8(in4, shufmask));
+ __m128i pack = _mm_unpacklo_epi64(pack1, pack2);
+ _mm_storeu_si128((__m128i *)latin1_output, pack);
+ latin1_output += 16;
+ buf += 16;
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start),
+ latin1_output);
+}
+/* end file src/westmere/sse_convert_utf32_to_latin1.cpp */
+/* begin file src/westmere/sse_convert_utf32_to_utf8.cpp */
+std::pair<const char32_t*, char*> sse_convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) {
+ const char32_t* end = buf + len;
+
+ const __m128i v_0000 = _mm_setzero_si128();//__m128 = 128 bits
+ const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800); //1111 1000 0000 0000
+ const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080); //1100 0000 1000 0000
+ const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80); //1111 1111 1000 0000
+ const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000); //1111 1111 1111 1111 0000 0000 0000 0000
+ const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff); //0111 1111 1111 1111 1111 1111 1111 1111
+ __m128i running_max = _mm_setzero_si128();
+ __m128i forbidden_bytemask = _mm_setzero_si128();
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) { //buf is a char32_t pointer, each char32_t has 4 bytes or 32 bits, thus buf + 16 * char_32t = 512 bits = 64 bytes
+ // We load two 16 bytes registers for a total of 32 bytes or 16 characters.
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);//These two values can hold only 8 UTF32 chars
+ running_max = _mm_max_epu32(
+ _mm_max_epu32(in, running_max), //take element-wise max char32_t from in and running_max vector
+ nextin); //and take element-wise max element from nextin and running_max vector
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m128i in_16 = _mm_packus_epi32(
+ _mm_and_si128(in, v_7fffffff),
+ _mm_and_si128(nextin, v_7fffffff)
+ );//in this context pack the two __m128 into a single
+ //By ensuring the highest bit is set to 0(&v_7fffffff), we're making sure all values are interpreted as non-negative, or specifically, the values are within the range of valid Unicode code points.
+ //remember : having leading byte 0 means a positive number by the two complements system. Unicode is well beneath the range where you'll start getting issues so that's OK.
+
+ // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp
+
+ // Check for ASCII fast path
+
+ // ASCII fast path!!!!
+ // We eagerly load another 32 bytes, hoping that they will be ASCII too.
+ // The intuition is that we try to collect 16 ASCII characters which requires
+ // a total of 64 bytes of input. If we fail, we just pass thirdin and fourthin
+ // as our new inputs.
+ if(_mm_testz_si128(in_16, v_ff80)) { //if the first two blocks are ASCII
+ __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2);
+ __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3);
+ running_max = _mm_max_epu32(_mm_max_epu32(thirdin, running_max), fourthin);//take the running max of all 4 vectors thus far
+ __m128i nextin_16 = _mm_packus_epi32(_mm_and_si128(thirdin, v_7fffffff), _mm_and_si128(fourthin, v_7fffffff));//pack into 1 vector, now you have two
+ if(!_mm_testz_si128(nextin_16, v_ff80)) { //checks if the second packed vector is ASCII, if not:
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in_16,in_16); //creates two copy of in_16 in 1 vector
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed); //put them into the output
+ // 3. adjust pointers
+ buf += 8; //the char32_t buffer pointer goes up 8 char32_t chars* 32 bits = 256 bits
+ utf8_output += 8; //same with output, e.g. lift the first two blocks alone.
+ // Proceed with next input
+ in_16 = nextin_16;
+ // We need to update in and nextin because they are used later.
+ in = thirdin;
+ nextin = fourthin;
+ } else {
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ // no bits set above 7th bit -- find out all the ASCII characters
+ const __m128i one_byte_bytemask = _mm_cmpeq_epi16( // this takes four bytes at a time and compares:
+ _mm_and_si128(in_16, v_ff80), // the vector that get only the first 9 bits of each 16-bit/2-byte units
+ v_0000 //
+ ); // they should be all zero if they are ASCII. E.g. ASCII in UTF32 is of format 0000 0000 0000 0XXX XXXX
+ // _mm_cmpeq_epi16 should now return a 1111 1111 1111 1111 for equals, and 0000 0000 0000 0000 if not for each 16-bit/2-byte units
+ const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask)); // collect the MSB from previous vector and put them into uint16_t mas
+
+ // no bits set above 11th bit
+ const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
+ const uint16_t one_or_two_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
+
+ if (one_or_two_bytes_bitmask == 0xffff) {
+ // case: all code units either produce 1 or 2 UTF-8 bytes (at least one produces 2 bytes)
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00); // 0001 1111 0000 0000
+ const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f); // 0000 0000 0011 1111
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m128i t0 = _mm_slli_epi16(in_16, 2); // shift packed vector by two
+ // t1 = [000a|aaaa|0000|0000]
+ const __m128i t1 = _mm_and_si128(t0, v_1f00); // potentital first utf8 byte
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m128i t2 = _mm_and_si128(in_16, v_003f);// potential second utf8 byte
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m128i t3 = _mm_or_si128(t1, t2); // first and second potential utf8 byte together
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m128i t4 = _mm_or_si128(t3, v_c080); // t3 | 1100 0000 1000 0000 = full potential 2-byte utf8 unit
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m128i utf8_unpacked = _mm_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB)
+ const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
+ const uint16_t m1 = static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
+ const uint8_t m2 = static_cast<uint8_t>((m0 | m1) & 0xff); // m2 = hdgcfbea
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+ }
+
+ // Check for overflow in packing
+
+ const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
+ if (saturation_bitmask == 0xffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+ const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
+ forbidden_bytemask = _mm_or_si128(forbidden_bytemask, _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800));
+
+ const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m128i s0 = _mm_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
+ const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m128i s4 = _mm_xor_si128(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
+ const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16_t mask = (one_byte_bitmask & 0x5555) |
+ (one_or_two_bytes_bitmask & 0xaaaa);
+ if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ } else {
+ // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xFFFF0000 )==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ // check for invalid input
+ const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
+ if(static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi32(_mm_max_epu32(running_max, v_10ffff), v_10ffff))) != 0xffff) {
+ return std::make_pair(nullptr, utf8_output);
+ }
+
+ if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf8_output); }
+
+ return std::make_pair(buf, utf8_output);
+}
+
+
+std::pair<result, char*> sse_convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) {
+
+ const char32_t* end = buf + len;
+ const char32_t* start = buf;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
+ const __m128i v_c080 = _mm_set1_epi16((uint16_t)0xc080);
+ const __m128i v_ff80 = _mm_set1_epi16((uint16_t)0xff80);
+ const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
+ const __m128i v_7fffffff = _mm_set1_epi32((uint32_t)0x7fffffff);
+ const __m128i v_10ffff = _mm_set1_epi32((uint32_t)0x10ffff);
+
+ const size_t safety_margin = 12; // to avoid overruns, see issue https://github.com/simdutf/simdutf/issues/92
+
+ while (buf + 16 + safety_margin <= end) {
+ // We load two 16 bytes registers for a total of 32 bytes or 16 characters.
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);
+
+ // Check for too large input
+ __m128i max_input = _mm_max_epu32(_mm_max_epu32(in, nextin), v_10ffff);
+ if(static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi32(max_input, v_10ffff))) != 0xffff) {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output);
+ }
+
+ // Pack 32-bit UTF-32 code units to 16-bit UTF-16 code units with unsigned saturation
+ __m128i in_16 = _mm_packus_epi32(_mm_and_si128(in, v_7fffffff), _mm_and_si128(nextin, v_7fffffff));
+
+ // Try to apply UTF-16 => UTF-8 from ./sse_convert_utf16_to_utf8.cpp
+
+ // Check for ASCII fast path
+ if(_mm_testz_si128(in_16, v_ff80)) { // ASCII fast path!!!!
+ // We eagerly load another 32 bytes, hoping that they will be ASCII too.
+ // The intuition is that we try to collect 16 ASCII characters which requires
+ // a total of 64 bytes of input. If we fail, we just pass thirdin and fourthin
+ // as our new inputs.
+ __m128i thirdin = _mm_loadu_si128((__m128i*)buf+2);
+ __m128i fourthin = _mm_loadu_si128((__m128i*)buf+3);
+ __m128i nextin_16 = _mm_packus_epi32(_mm_and_si128(thirdin, v_7fffffff), _mm_and_si128(fourthin, v_7fffffff));
+ if(!_mm_testz_si128(nextin_16, v_ff80)) {
+ // 1. pack the bytes
+ // obviously suboptimal.
+ const __m128i utf8_packed = _mm_packus_epi16(in_16,in_16);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 8;
+ utf8_output += 8;
+ // Proceed with next input
+ in_16 = nextin_16;
+ __m128i next_max_input = _mm_max_epu32(_mm_max_epu32(thirdin, fourthin), v_10ffff);
+ if(static_cast<uint16_t>(_mm_movemask_epi8(_mm_cmpeq_epi32(next_max_input, v_10ffff))) != 0xffff) {
+ return std::make_pair(result(error_code::TOO_LARGE, buf - start), utf8_output);
+ }
+ // We need to update in and nextin because they are used later.
+ in = thirdin;
+ nextin = fourthin;
+ } else {
+ // 1. pack the bytes
+ const __m128i utf8_packed = _mm_packus_epi16(in_16, nextin_16);
+ // 2. store (16 bytes)
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+ // 3. adjust pointers
+ buf += 16;
+ utf8_output += 16;
+ continue; // we are done for this round!
+ }
+ }
+
+ // no bits set above 7th bit
+ const __m128i one_byte_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_ff80), v_0000);
+ const uint16_t one_byte_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_byte_bytemask));
+
+ // no bits set above 11th bit
+ const __m128i one_or_two_bytes_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_0000);
+ const uint16_t one_or_two_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(one_or_two_bytes_bytemask));
+
+ if (one_or_two_bytes_bitmask == 0xffff) {
+ // case: all code units either produce 1 or 2 UTF-8 bytes (at least one produces 2 bytes)
+ // 1. prepare 2-byte values
+ // input 16-bit word : [0000|0aaa|aabb|bbbb] x 8
+ // expected output : [110a|aaaa|10bb|bbbb] x 8
+ const __m128i v_1f00 = _mm_set1_epi16((int16_t)0x1f00);
+ const __m128i v_003f = _mm_set1_epi16((int16_t)0x003f);
+
+ // t0 = [000a|aaaa|bbbb|bb00]
+ const __m128i t0 = _mm_slli_epi16(in_16, 2);
+ // t1 = [000a|aaaa|0000|0000]
+ const __m128i t1 = _mm_and_si128(t0, v_1f00);
+ // t2 = [0000|0000|00bb|bbbb]
+ const __m128i t2 = _mm_and_si128(in_16, v_003f);
+ // t3 = [000a|aaaa|00bb|bbbb]
+ const __m128i t3 = _mm_or_si128(t1, t2);
+ // t4 = [110a|aaaa|10bb|bbbb]
+ const __m128i t4 = _mm_or_si128(t3, v_c080);
+
+ // 2. merge ASCII and 2-byte codewords
+ const __m128i utf8_unpacked = _mm_blendv_epi8(t4, in_16, one_byte_bytemask);
+
+ // 3. prepare bitmask for 8-bit lookup
+ // one_byte_bitmask = hhggffeeddccbbaa -- the bits are doubled (h - MSB, a - LSB)
+ const uint16_t m0 = one_byte_bitmask & 0x5555; // m0 = 0h0g0f0e0d0c0b0a
+ const uint16_t m1 = static_cast<uint16_t>(m0 >> 7); // m1 = 00000000h0g0f0e0
+ const uint8_t m2 = static_cast<uint8_t>((m0 | m1) & 0xff); // m2 = hdgcfbea
+ // 4. pack the bytes
+ const uint8_t* row = &simdutf::tables::utf16_to_utf8::pack_1_2_utf8_bytes[m2][0];
+ const __m128i shuffle = _mm_loadu_si128((__m128i*)(row + 1));
+ const __m128i utf8_packed = _mm_shuffle_epi8(utf8_unpacked, shuffle);
+
+ // 5. store bytes
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_packed);
+
+ // 6. adjust pointers
+ buf += 8;
+ utf8_output += row[0];
+ continue;
+ }
+
+
+ // Check for overflow in packing
+ const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
+
+ if (saturation_bitmask == 0xffff) {
+ // case: code units from register produce either 1, 2 or 3 UTF-8 bytes
+
+ // Check for illegal surrogate code units
+ const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
+ const __m128i forbidden_bytemask = _mm_cmpeq_epi16(_mm_and_si128(in_16, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf8_output);
+ }
+
+ const __m128i dup_even = _mm_setr_epi16(0x0000, 0x0202, 0x0404, 0x0606,
+ 0x0808, 0x0a0a, 0x0c0c, 0x0e0e);
+
+ /* In this branch we handle three cases:
+ 1. [0000|0000|0ccc|cccc] => [0ccc|cccc] - single UFT-8 byte
+ 2. [0000|0bbb|bbcc|cccc] => [110b|bbbb], [10cc|cccc] - two UTF-8 bytes
+ 3. [aaaa|bbbb|bbcc|cccc] => [1110|aaaa], [10bb|bbbb], [10cc|cccc] - three UTF-8 bytes
+
+ We expand the input word (16-bit) into two code units (32-bit), thus
+ we have room for four bytes. However, we need five distinct bit
+ layouts. Note that the last byte in cases #2 and #3 is the same.
+
+ We precompute byte 1 for case #1 and the common byte for cases #2 & #3
+ in register t2.
+
+ We precompute byte 1 for case #3 and -- **conditionally** -- precompute
+ either byte 1 for case #2 or byte 2 for case #3. Note that they
+ differ by exactly one bit.
+
+ Finally from these two code units we build proper UTF-8 sequence, taking
+ into account the case (i.e, the number of bytes to write).
+ */
+ /**
+ * Given [aaaa|bbbb|bbcc|cccc] our goal is to produce:
+ * t2 => [0ccc|cccc] [10cc|cccc]
+ * s4 => [1110|aaaa] ([110b|bbbb] OR [10bb|bbbb])
+ */
+#define simdutf_vec(x) _mm_set1_epi16(static_cast<uint16_t>(x))
+ // [aaaa|bbbb|bbcc|cccc] => [bbcc|cccc|bbcc|cccc]
+ const __m128i t0 = _mm_shuffle_epi8(in_16, dup_even);
+ // [bbcc|cccc|bbcc|cccc] => [00cc|cccc|0bcc|cccc]
+ const __m128i t1 = _mm_and_si128(t0, simdutf_vec(0b0011111101111111));
+ // [00cc|cccc|0bcc|cccc] => [10cc|cccc|0bcc|cccc]
+ const __m128i t2 = _mm_or_si128 (t1, simdutf_vec(0b1000000000000000));
+
+ // [aaaa|bbbb|bbcc|cccc] => [0000|aaaa|bbbb|bbcc]
+ const __m128i s0 = _mm_srli_epi16(in_16, 4);
+ // [0000|aaaa|bbbb|bbcc] => [0000|aaaa|bbbb|bb00]
+ const __m128i s1 = _mm_and_si128(s0, simdutf_vec(0b0000111111111100));
+ // [0000|aaaa|bbbb|bb00] => [00bb|bbbb|0000|aaaa]
+ const __m128i s2 = _mm_maddubs_epi16(s1, simdutf_vec(0x0140));
+ // [00bb|bbbb|0000|aaaa] => [11bb|bbbb|1110|aaaa]
+ const __m128i s3 = _mm_or_si128(s2, simdutf_vec(0b1100000011100000));
+ const __m128i m0 = _mm_andnot_si128(one_or_two_bytes_bytemask, simdutf_vec(0b0100000000000000));
+ const __m128i s4 = _mm_xor_si128(s3, m0);
+#undef simdutf_vec
+
+ // 4. expand code units 16-bit => 32-bit
+ const __m128i out0 = _mm_unpacklo_epi16(t2, s4);
+ const __m128i out1 = _mm_unpackhi_epi16(t2, s4);
+
+ // 5. compress 32-bit code units into 1, 2 or 3 bytes -- 2 x shuffle
+ const uint16_t mask = (one_byte_bitmask & 0x5555) |
+ (one_or_two_bytes_bitmask & 0xaaaa);
+ if(mask == 0) {
+ // We only have three-byte code units. Use fast path.
+ const __m128i shuffle = _mm_setr_epi8(2,3,1,6,7,5,10,11,9,14,15,13,-1,-1,-1,-1);
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle);
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle);
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += 12;
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += 12;
+ buf += 8;
+ continue;
+ }
+ const uint8_t mask0 = uint8_t(mask);
+
+ const uint8_t* row0 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask0][0];
+ const __m128i shuffle0 = _mm_loadu_si128((__m128i*)(row0 + 1));
+ const __m128i utf8_0 = _mm_shuffle_epi8(out0, shuffle0);
+
+ const uint8_t mask1 = static_cast<uint8_t>(mask >> 8);
+
+ const uint8_t* row1 = &simdutf::tables::utf16_to_utf8::pack_1_2_3_utf8_bytes[mask1][0];
+ const __m128i shuffle1 = _mm_loadu_si128((__m128i*)(row1 + 1));
+ const __m128i utf8_1 = _mm_shuffle_epi8(out1, shuffle1);
+
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_0);
+ utf8_output += row0[0];
+ _mm_storeu_si128((__m128i*)utf8_output, utf8_1);
+ utf8_output += row1[0];
+
+ buf += 8;
+ } else {
+ // case: at least one 32-bit word produce a surrogate pair in UTF-16 <=> will produce four UTF-8 bytes
+ // Let us do a scalar fallback.
+ // It may seem wasteful to use scalar code, but being efficient with SIMD
+ // in the presence of surrogate pairs may require non-trivial tables.
+ size_t forward = 15;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFFFF80)==0) {
+ *utf8_output++ = char(word);
+ } else if((word & 0xFFFFF800)==0) {
+ *utf8_output++ = char((word>>6) | 0b11000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else if((word &0xFFFF0000 )==0) {
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf8_output); }
+ *utf8_output++ = char((word>>12) | 0b11100000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ } else {
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf- start + k), utf8_output); }
+ *utf8_output++ = char((word>>18) | 0b11110000);
+ *utf8_output++ = char(((word>>12) & 0b111111) | 0b10000000);
+ *utf8_output++ = char(((word>>6) & 0b111111) | 0b10000000);
+ *utf8_output++ = char((word & 0b111111) | 0b10000000);
+ }
+ }
+ buf += k;
+ }
+ } // while
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf8_output);
+}
+/* end file src/westmere/sse_convert_utf32_to_utf8.cpp */
+/* begin file src/westmere/sse_convert_utf32_to_utf16.cpp */
+template <endianness big_endian>
+std::pair<const char32_t*, char16_t*> sse_convert_utf32_to_utf16(const char32_t* buf, size_t len, char16_t* utf16_output) {
+
+ const char32_t* end = buf + len;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
+ __m128i forbidden_bytemask = _mm_setzero_si128();
+
+ while (buf + 8 <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);
+ const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
+
+ // Check if no bits set above 16th
+ if (saturation_bitmask == 0xffff) {
+ // Pack UTF-32 to UTF-16
+ __m128i utf16_packed = _mm_packus_epi32(in, nextin);
+
+ const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
+ forbidden_bytemask = _mm_or_si128(forbidden_bytemask, _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800));
+
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(nullptr, utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(nullptr, utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ // check for invalid input
+ if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) { return std::make_pair(nullptr, utf16_output); }
+
+ return std::make_pair(buf, utf16_output);
+}
+
+
+template <endianness big_endian>
+std::pair<result, char16_t*> sse_convert_utf32_to_utf16_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) {
+ const char32_t* start = buf;
+ const char32_t* end = buf + len;
+
+ const __m128i v_0000 = _mm_setzero_si128();
+ const __m128i v_ffff0000 = _mm_set1_epi32((int32_t)0xffff0000);
+
+ while (buf + 8 <= end) {
+ __m128i in = _mm_loadu_si128((__m128i*)buf);
+ __m128i nextin = _mm_loadu_si128((__m128i*)buf+1);
+ const __m128i saturation_bytemask = _mm_cmpeq_epi32(_mm_and_si128(_mm_or_si128(in, nextin), v_ffff0000), v_0000);
+ const uint32_t saturation_bitmask = static_cast<uint32_t>(_mm_movemask_epi8(saturation_bytemask));
+
+ // Check if no bits set above 16th
+ if (saturation_bitmask == 0xffff) {
+ // Pack UTF-32 to UTF-16
+ __m128i utf16_packed = _mm_packus_epi32(in, nextin);
+
+ const __m128i v_f800 = _mm_set1_epi16((uint16_t)0xf800);
+ const __m128i v_d800 = _mm_set1_epi16((uint16_t)0xd800);
+ const __m128i forbidden_bytemask = _mm_cmpeq_epi16(_mm_and_si128(utf16_packed, v_f800), v_d800);
+ if (static_cast<uint32_t>(_mm_movemask_epi8(forbidden_bytemask)) != 0) {
+ return std::make_pair(result(error_code::SURROGATE, buf - start), utf16_output);
+ }
+
+ if (big_endian) {
+ const __m128i swap = _mm_setr_epi8(1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14);
+ utf16_packed = _mm_shuffle_epi8(utf16_packed, swap);
+ }
+
+ _mm_storeu_si128((__m128i*)utf16_output, utf16_packed);
+ utf16_output += 8;
+ buf += 8;
+ } else {
+ size_t forward = 7;
+ size_t k = 0;
+ if(size_t(end - buf) < forward + 1) { forward = size_t(end - buf - 1);}
+ for(; k < forward; k++) {
+ uint32_t word = buf[k];
+ if((word & 0xFFFF0000)==0) {
+ // will not generate a surrogate pair
+ if (word >= 0xD800 && word <= 0xDFFF) { return std::make_pair(result(error_code::SURROGATE, buf - start + k), utf16_output); }
+ *utf16_output++ = big_endian ? char16_t((uint16_t(word) >> 8) | (uint16_t(word) << 8)) : char16_t(word);
+ } else {
+ // will generate a surrogate pair
+ if (word > 0x10FFFF) { return std::make_pair(result(error_code::TOO_LARGE, buf - start + k), utf16_output); }
+ word -= 0x10000;
+ uint16_t high_surrogate = uint16_t(0xD800 + (word >> 10));
+ uint16_t low_surrogate = uint16_t(0xDC00 + (word & 0x3FF));
+ if (big_endian) {
+ high_surrogate = uint16_t((high_surrogate >> 8) | (high_surrogate << 8));
+ low_surrogate = uint16_t((low_surrogate >> 8) | (low_surrogate << 8));
+ }
+ *utf16_output++ = char16_t(high_surrogate);
+ *utf16_output++ = char16_t(low_surrogate);
+ }
+ }
+ buf += k;
+ }
+ }
+
+ return std::make_pair(result(error_code::SUCCESS, buf - start), utf16_output);
+}
+/* end file src/westmere/sse_convert_utf32_to_utf16.cpp */
+/* begin file src/westmere/sse_base64.cpp */
+/**
+ * References and further reading:
+ *
+ * Wojciech Muła, Daniel Lemire, Base64 encoding and decoding at almost the
+ * speed of a memory copy, Software: Practice and Experience 50 (2), 2020.
+ * https://arxiv.org/abs/1910.05109
+ *
+ * Wojciech Muła, Daniel Lemire, Faster Base64 Encoding and Decoding using AVX2
+ * Instructions, ACM Transactions on the Web 12 (3), 2018.
+ * https://arxiv.org/abs/1704.00605
+ *
+ * Simon Josefsson. 2006. The Base16, Base32, and Base64 Data Encodings.
+ * https://tools.ietf.org/html/rfc4648. (2006). Internet Engineering Task Force,
+ * Request for Comments: 4648.
+ *
+ * Alfred Klomp. 2014a. Fast Base64 encoding/decoding with SSE vectorization.
+ * http://www.alfredklomp.com/programming/sse-base64/. (2014).
+ *
+ * Alfred Klomp. 2014b. Fast Base64 stream encoder/decoder in C99, with SIMD
+ * acceleration. https://github.com/aklomp/base64. (2014).
+ *
+ * Hanson Char. 2014. A Fast and Correct Base 64 Codec. (2014).
+ * https://aws.amazon.com/blogs/developer/a-fast-and-correct-base-64-codec/
+ *
+ * Nick Kopp. 2013. Base64 Encoding on a GPU.
+ * https://www.codeproject.com/Articles/276993/Base-Encoding-on-a-GPU. (2013).
+ */
+template <bool base64_url> __m128i lookup_pshufb_improved(const __m128i input) {
+ // credit: Wojciech Muła
+ // reduce 0..51 -> 0
+ // 52..61 -> 1 .. 10
+ // 62 -> 11
+ // 63 -> 12
+ __m128i result = _mm_subs_epu8(input, _mm_set1_epi8(51));
+
+ // distinguish between ranges 0..25 and 26..51:
+ // 0 .. 25 -> remains 0
+ // 26 .. 51 -> becomes 13
+ const __m128i less = _mm_cmpgt_epi8(_mm_set1_epi8(26), input);
+ result = _mm_or_si128(result, _mm_and_si128(less, _mm_set1_epi8(13)));
+
+ __m128i shift_LUT;
+ if (base64_url) {
+ shift_LUT = _mm_setr_epi8('a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '-' - 62, '_' - 63, 'A', 0, 0);
+ } else {
+ shift_LUT = _mm_setr_epi8('a' - 26, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '0' - 52, '0' - 52, '0' - 52, '0' - 52,
+ '0' - 52, '+' - 62, '/' - 63, 'A', 0, 0);
+ }
+
+ // read shift
+ result = _mm_shuffle_epi8(shift_LUT, result);
+
+ return _mm_add_epi8(result, input);
+}
+
+template <base64_options options>
+size_t encode_base64(char *dst, const char *src, size_t srclen) {
+ // credit: Wojciech Muła
+ // SSE (lookup: pshufb improved unrolled)
+ const uint8_t *input = (const uint8_t *)src;
+
+ uint8_t *out = (uint8_t *)dst;
+ const __m128i shuf =
+ _mm_set_epi8(10, 11, 9, 10, 7, 8, 6, 7, 4, 5, 3, 4, 1, 2, 0, 1);
+
+ size_t i = 0;
+ for (; i + 52 <= srclen; i += 48) {
+ __m128i in0 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 0));
+ __m128i in1 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 1));
+ __m128i in2 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 2));
+ __m128i in3 = _mm_loadu_si128(
+ reinterpret_cast<const __m128i *>(input + i + 4 * 3 * 3));
+
+ in0 = _mm_shuffle_epi8(in0, shuf);
+ in1 = _mm_shuffle_epi8(in1, shuf);
+ in2 = _mm_shuffle_epi8(in2, shuf);
+ in3 = _mm_shuffle_epi8(in3, shuf);
+
+ const __m128i t0_0 = _mm_and_si128(in0, _mm_set1_epi32(0x0fc0fc00));
+ const __m128i t0_1 = _mm_and_si128(in1, _mm_set1_epi32(0x0fc0fc00));
+ const __m128i t0_2 = _mm_and_si128(in2, _mm_set1_epi32(0x0fc0fc00));
+ const __m128i t0_3 = _mm_and_si128(in3, _mm_set1_epi32(0x0fc0fc00));
+
+ const __m128i t1_0 = _mm_mulhi_epu16(t0_0, _mm_set1_epi32(0x04000040));
+ const __m128i t1_1 = _mm_mulhi_epu16(t0_1, _mm_set1_epi32(0x04000040));
+ const __m128i t1_2 = _mm_mulhi_epu16(t0_2, _mm_set1_epi32(0x04000040));
+ const __m128i t1_3 = _mm_mulhi_epu16(t0_3, _mm_set1_epi32(0x04000040));
+
+ const __m128i t2_0 = _mm_and_si128(in0, _mm_set1_epi32(0x003f03f0));
+ const __m128i t2_1 = _mm_and_si128(in1, _mm_set1_epi32(0x003f03f0));
+ const __m128i t2_2 = _mm_and_si128(in2, _mm_set1_epi32(0x003f03f0));
+ const __m128i t2_3 = _mm_and_si128(in3, _mm_set1_epi32(0x003f03f0));
+
+ const __m128i t3_0 = _mm_mullo_epi16(t2_0, _mm_set1_epi32(0x01000010));
+ const __m128i t3_1 = _mm_mullo_epi16(t2_1, _mm_set1_epi32(0x01000010));
+ const __m128i t3_2 = _mm_mullo_epi16(t2_2, _mm_set1_epi32(0x01000010));
+ const __m128i t3_3 = _mm_mullo_epi16(t2_3, _mm_set1_epi32(0x01000010));
+
+ const __m128i input0 = _mm_or_si128(t1_0, t3_0);
+ const __m128i input1 = _mm_or_si128(t1_1, t3_1);
+ const __m128i input2 = _mm_or_si128(t1_2, t3_2);
+ const __m128i input3 = _mm_or_si128(t1_3, t3_3);
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(out),
+ lookup_pshufb_improved<options & base64_url>(input0));
+ out += 16;
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(out),
+ lookup_pshufb_improved<options & base64_url>(input1));
+ out += 16;
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(out),
+ lookup_pshufb_improved<options & base64_url>(input2));
+ out += 16;
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(out),
+ lookup_pshufb_improved<options & base64_url>(input3));
+ out += 16;
+ }
+ for (; i + 16 <= srclen; i += 12) {
+
+ __m128i in = _mm_loadu_si128(reinterpret_cast<const __m128i *>(input + i));
+
+ // bytes from groups A, B and C are needed in separate 32-bit lanes
+ // in = [DDDD|CCCC|BBBB|AAAA]
+ //
+ // an input triplet has layout
+ // [????????|ccdddddd|bbbbcccc|aaaaaabb]
+ // byte 3 byte 2 byte 1 byte 0 -- byte 3 comes from the next
+ // triplet
+ //
+ // shuffling changes the order of bytes: 1, 0, 2, 1
+ // [bbbbcccc|ccdddddd|aaaaaabb|bbbbcccc]
+ // ^^^^ ^^^^^^^^ ^^^^^^^^ ^^^^
+ // processed bits
+ in = _mm_shuffle_epi8(in, shuf);
+
+ // unpacking
+
+ // t0 = [0000cccc|cc000000|aaaaaa00|00000000]
+ const __m128i t0 = _mm_and_si128(in, _mm_set1_epi32(0x0fc0fc00));
+ // t1 = [00000000|00cccccc|00000000|00aaaaaa]
+ // (c * (1 << 10), a * (1 << 6)) >> 16 (note: an unsigned
+ // multiplication)
+ const __m128i t1 = _mm_mulhi_epu16(t0, _mm_set1_epi32(0x04000040));
+
+ // t2 = [00000000|00dddddd|000000bb|bbbb0000]
+ const __m128i t2 = _mm_and_si128(in, _mm_set1_epi32(0x003f03f0));
+ // t3 = [00dddddd|00000000|00bbbbbb|00000000](
+ // (d * (1 << 8), b * (1 << 4))
+ const __m128i t3 = _mm_mullo_epi16(t2, _mm_set1_epi32(0x01000010));
+
+ // res = [00dddddd|00cccccc|00bbbbbb|00aaaaaa] = t1 | t3
+ const __m128i indices = _mm_or_si128(t1, t3);
+
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(out),
+ lookup_pshufb_improved<options & base64_url>(indices));
+ out += 16;
+ }
+
+ return i / 3 * 4 + scalar::base64::tail_encode_base64((char *)out, src + i,
+ srclen - i, options);
+}
+static inline void compress(__m128i data, uint16_t mask, char *output) {
+ if (mask == 0) {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output), data);
+ return;
+ }
+
+ // this particular implementation was inspired by work done by @animetosho
+ // we do it in two steps, first 8 bytes and then second 8 bytes
+ uint8_t mask1 = uint8_t(mask); // least significant 8 bits
+ uint8_t mask2 = uint8_t(mask >> 8); // most significant 8 bits
+ // next line just loads the 64-bit values thintable_epi8[mask1] and
+ // thintable_epi8[mask2] into a 128-bit register, using only
+ // two instructions on most compilers.
+
+ __m128i shufmask = _mm_set_epi64x(tables::base64::thintable_epi8[mask2],
+ tables::base64::thintable_epi8[mask1]);
+ // we increment by 0x08 the second half of the mask
+ shufmask =
+ _mm_add_epi8(shufmask, _mm_set_epi32(0x08080808, 0x08080808, 0, 0));
+ // this is the version "nearly pruned"
+ __m128i pruned = _mm_shuffle_epi8(data, shufmask);
+ // we still need to put the two halves together.
+ // we compute the popcount of the first half:
+ int pop1 = tables::base64::BitsSetTable256mul2[mask1];
+ // then load the corresponding mask, what it does is to write
+ // only the first pop1 bytes from the first 8 bytes, and then
+ // it fills in with the bytes from the second 8 bytes + some filling
+ // at the end.
+ __m128i compactmask = _mm_loadu_si128(reinterpret_cast<const __m128i *>(
+ tables::base64::pshufb_combine_table + pop1 * 8));
+ __m128i answer = _mm_shuffle_epi8(pruned, compactmask);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output), answer);
+}
+
+struct block64 {
+ __m128i chunks[4];
+};
+
+template <bool base64_url>
+static inline uint16_t to_base64_mask(__m128i *src, bool *error) {
+ const __m128i ascii_space_tbl =
+ _mm_setr_epi8(0x20, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x9, 0xa, 0x0,
+ 0xc, 0xd, 0x0, 0x0);
+ // credit: aqrit
+ __m128i delta_asso;
+ if (base64_url) {
+ delta_asso = _mm_setr_epi8(0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x0, 0x0,
+ 0x0, 0x0, 0x0, 0xF, 0x0, 0xF);
+ } else {
+
+ delta_asso = _mm_setr_epi8(0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x0F);
+ }
+ __m128i delta_values;
+ if (base64_url) {
+ delta_values = _mm_setr_epi8(0x0, 0x0, 0x0, 0x13, 0x4, uint8_t(0xBF),
+ uint8_t(0xBF), uint8_t(0xB9), uint8_t(0xB9),
+ 0x0, 0x11, uint8_t(0xC3), uint8_t(0xBF),
+ uint8_t(0xE0), uint8_t(0xB9), uint8_t(0xB9));
+ } else {
+
+ delta_values =
+ _mm_setr_epi8(int8_t(0x00), int8_t(0x00), int8_t(0x00), int8_t(0x13),
+ int8_t(0x04), int8_t(0xBF), int8_t(0xBF), int8_t(0xB9),
+ int8_t(0xB9), int8_t(0x00), int8_t(0x10), int8_t(0xC3),
+ int8_t(0xBF), int8_t(0xBF), int8_t(0xB9), int8_t(0xB9));
+ }
+ __m128i check_asso;
+ if (base64_url) {
+ check_asso = _mm_setr_epi8(0xD, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1,
+ 0x3, 0x7, 0xB, 0x6, 0xB, 0x12);
+ } else {
+
+ check_asso = _mm_setr_epi8(0x0D, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
+ 0x01, 0x01, 0x03, 0x07, 0x0B, 0x0B, 0x0B, 0x0F);
+ }
+ __m128i check_values;
+ if (base64_url) {
+ check_values = _mm_setr_epi8(0x0, uint8_t(0x80), uint8_t(0x80),
+ uint8_t(0x80), uint8_t(0xCF), uint8_t(0xBF),
+ uint8_t(0xD3), uint8_t(0xA6), uint8_t(0xB5),
+ uint8_t(0x86), uint8_t(0xD0), uint8_t(0x80),
+ uint8_t(0xB0), uint8_t(0x80), 0x0, 0x0);
+ } else {
+
+ check_values =
+ _mm_setr_epi8(int8_t(0x80), int8_t(0x80), int8_t(0x80), int8_t(0x80),
+ int8_t(0xCF), int8_t(0xBF), int8_t(0xD5), int8_t(0xA6),
+ int8_t(0xB5), int8_t(0x86), int8_t(0xD1), int8_t(0x80),
+ int8_t(0xB1), int8_t(0x80), int8_t(0x91), int8_t(0x80));
+ }
+ const __m128i shifted = _mm_srli_epi32(*src, 3);
+
+ const __m128i delta_hash =
+ _mm_avg_epu8(_mm_shuffle_epi8(delta_asso, *src), shifted);
+ const __m128i check_hash =
+ _mm_avg_epu8(_mm_shuffle_epi8(check_asso, *src), shifted);
+
+ const __m128i out =
+ _mm_adds_epi8(_mm_shuffle_epi8(delta_values, delta_hash), *src);
+ const __m128i chk =
+ _mm_adds_epi8(_mm_shuffle_epi8(check_values, check_hash), *src);
+ const int mask = _mm_movemask_epi8(chk);
+ if (mask) {
+ __m128i ascii_space =
+ _mm_cmpeq_epi8(_mm_shuffle_epi8(ascii_space_tbl, *src), *src);
+ *error |= (mask != _mm_movemask_epi8(ascii_space));
+ }
+ *src = out;
+ return (uint16_t)mask;
+}
+
+template <bool base64_url>
+static inline uint64_t to_base64_mask(block64 *b, bool *error) {
+ *error = 0;
+ uint64_t m0 = to_base64_mask<base64_url>(&b->chunks[0], error);
+ uint64_t m1 = to_base64_mask<base64_url>(&b->chunks[1], error);
+ uint64_t m2 = to_base64_mask<base64_url>(&b->chunks[2], error);
+ uint64_t m3 = to_base64_mask<base64_url>(&b->chunks[3], error);
+ return m0 | (m1 << 16) | (m2 << 32) | (m3 << 48);
+}
+
+static inline void copy_block(block64 *b, char *output) {
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output), b->chunks[0]);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 16), b->chunks[1]);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 32), b->chunks[2]);
+ _mm_storeu_si128(reinterpret_cast<__m128i *>(output + 48), b->chunks[3]);
+}
+
+static inline uint64_t compress_block(block64 *b, uint64_t mask, char *output) {
+ uint64_t nmask = ~mask;
+ compress(b->chunks[0], uint16_t(mask), output);
+ compress(b->chunks[1], uint16_t(mask >> 16),
+ output + _mm_popcnt_u64(nmask & 0xFFFF));
+ compress(b->chunks[2], uint16_t(mask >> 32),
+ output + _mm_popcnt_u64(nmask & 0xFFFFFFFF));
+ compress(b->chunks[3], uint16_t(mask >> 48),
+ output + _mm_popcnt_u64(nmask & 0xFFFFFFFFFFFFULL));
+ return _mm_popcnt_u64(nmask);
+}
+
+// The caller of this function is responsible to ensure that there are 64 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char *src) {
+ b->chunks[0] = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
+ b->chunks[1] = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 16));
+ b->chunks[2] = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 32));
+ b->chunks[3] = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 48));
+}
+
+// The caller of this function is responsible to ensure that there are 128 bytes available
+// from reading at src. The data is read into a block64 structure.
+static inline void load_block(block64 *b, const char16_t *src) {
+ __m128i m1 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src));
+ __m128i m2 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 8));
+ __m128i m3 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 16));
+ __m128i m4 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 24));
+ __m128i m5 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 32));
+ __m128i m6 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 40));
+ __m128i m7 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 48));
+ __m128i m8 = _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 56));
+ b->chunks[0] = _mm_packus_epi16(m1, m2);
+ b->chunks[1] = _mm_packus_epi16(m3, m4);
+ b->chunks[2] = _mm_packus_epi16(m5, m6);
+ b->chunks[3] = _mm_packus_epi16(m7, m8);
+}
+
+static inline void base64_decode(char *out, __m128i str) {
+ // credit: aqrit
+
+ const __m128i pack_shuffle =
+ _mm_setr_epi8(2, 1, 0, 6, 5, 4, 10, 9, 8, 14, 13, 12, -1, -1, -1, -1);
+
+ const __m128i t0 = _mm_maddubs_epi16(str, _mm_set1_epi32(0x01400140));
+ const __m128i t1 = _mm_madd_epi16(t0, _mm_set1_epi32(0x00011000));
+ const __m128i t2 = _mm_shuffle_epi8(t1, pack_shuffle);
+ // Store the output:
+ // this writes 16 bytes, but we only need 12.
+ _mm_storeu_si128((__m128i *)out, t2);
+}
+// decode 64 bytes and output 48 bytes
+static inline void base64_decode_block(char *out, const char *src) {
+ base64_decode(out, _mm_loadu_si128(reinterpret_cast<const __m128i *>(src)));
+ base64_decode(out + 12,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 16)));
+ base64_decode(out + 24,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 32)));
+ base64_decode(out + 36,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 48)));
+}
+static inline void base64_decode_block_safe(char *out, const char *src) {
+ base64_decode(out, _mm_loadu_si128(reinterpret_cast<const __m128i *>(src)));
+ base64_decode(out + 12,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 16)));
+ base64_decode(out + 24,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 32)));
+ char buffer[16];
+ base64_decode(buffer,
+ _mm_loadu_si128(reinterpret_cast<const __m128i *>(src + 48)));
+ std::memcpy(out + 36, buffer, 12);
+}
+static inline void base64_decode_block(char *out, block64 *b) {
+ base64_decode(out, b->chunks[0]);
+ base64_decode(out + 12, b->chunks[1]);
+ base64_decode(out + 24, b->chunks[2]);
+ base64_decode(out + 36, b->chunks[3]);
+}
+static inline void base64_decode_block_safe(char *out, block64 *b) {
+ base64_decode(out, b->chunks[0]);
+ base64_decode(out + 12, b->chunks[1]);
+ base64_decode(out + 24, b->chunks[2]);
+ char buffer[16];
+ base64_decode(buffer, b->chunks[3]);
+ std::memcpy(out + 36, buffer, 12);
+}
+
+template <bool base64_url, typename chartype>
+result compress_decode_base64(char *dst, const chartype *src, size_t srclen,
+ base64_options options) {
+ const uint8_t *to_base64 = base64_url ? tables::base64::to_base64_url_value
+ : tables::base64::to_base64_value;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ size_t equalsigns = 0;
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 1;
+ // skip trailing spaces
+ while (srclen > 0 && to_base64[uint8_t(src[srclen - 1])] == 64) {
+ srclen--;
+ }
+ if (srclen > 0 && src[srclen - 1] == '=') {
+ srclen--;
+ equalsigns = 2;
+ }
+ }
+ char *end_of_safe_64byte_zone =
+ (srclen + 3) / 4 * 3 >= 63 ? dst + (srclen + 3) / 4 * 3 - 63 : dst;
+
+ const chartype *const srcinit = src;
+ const char *const dstinit = dst;
+ const chartype *const srcend = src + srclen;
+
+ constexpr size_t block_size = 6;
+ static_assert(block_size >= 2, "block should of size 2 or more");
+ char buffer[block_size * 64];
+ char *bufferptr = buffer;
+ if (srclen >= 64) {
+ const chartype *const srcend64 = src + srclen - 64;
+ while (src <= srcend64) {
+ block64 b;
+ load_block(&b, src);
+ src += 64;
+ bool error = false;
+ uint64_t badcharmask = to_base64_mask<base64_url>(&b, &error);
+ if (error) {
+ src -= 64;
+ while (src < srcend && to_base64[uint8_t(*src)] <= 64) {
+ src++;
+ }
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ if (badcharmask != 0) {
+ // optimization opportunity: check for simple masks like those made of
+ // continuous 1s followed by continuous 0s. And masks containing a
+ // single bad character.
+ bufferptr += compress_block(&b, badcharmask, bufferptr);
+ } else if (bufferptr != buffer) {
+ copy_block(&b, bufferptr);
+ bufferptr += 64;
+ } else {
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, &b);
+ } else {
+ base64_decode_block(dst, &b);
+ }
+ dst += 48;
+ }
+ if (bufferptr >= (block_size - 1) * 64 + buffer) {
+ for (size_t i = 0; i < (block_size - 2); i++) {
+ base64_decode_block(dst, buffer + i * 64);
+ dst += 48;
+ }
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, buffer + (block_size - 2) * 64);
+ } else {
+ base64_decode_block(dst, buffer + (block_size - 2) * 64);
+ }
+ dst += 48;
+ std::memcpy(buffer, buffer + (block_size - 1) * 64,
+ 64); // 64 might be too much
+ bufferptr -= (block_size - 1) * 64;
+ }
+ }
+ }
+
+ char *buffer_start = buffer;
+ // Optimization note: if this is almost full, then it is worth our
+ // time, otherwise, we should just decode directly.
+ int last_block = (int)((bufferptr - buffer_start) % 64);
+ if (last_block != 0 && srcend - src + last_block >= 64) {
+ while ((bufferptr - buffer_start) % 64 != 0 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ *bufferptr = char(val);
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ bufferptr += (val <= 63);
+ src++;
+ }
+ }
+
+ for (; buffer_start + 64 <= bufferptr; buffer_start += 64) {
+ if (dst >= end_of_safe_64byte_zone) {
+ base64_decode_block_safe(dst, buffer_start);
+ } else {
+ base64_decode_block(dst, buffer_start);
+ }
+ dst += 48;
+ }
+ if ((bufferptr - buffer_start) % 64 != 0) {
+ while (buffer_start + 4 < bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 4);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ if (buffer_start + 4 <= bufferptr) {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+
+ dst += 3;
+ buffer_start += 4;
+ }
+ // we may have 1, 2 or 3 bytes left and we need to decode them so let us
+ // bring in src content
+ int leftover = int(bufferptr - buffer_start);
+ if (leftover > 0) {
+ while (leftover < 4 && src < srcend) {
+ uint8_t val = to_base64[uint8_t(*src)];
+ if (val > 64) {
+ return {error_code::INVALID_BASE64_CHARACTER, size_t(src - srcinit)};
+ }
+ buffer_start[leftover] = char(val);
+ leftover += (val <= 63);
+ src++;
+ }
+
+ if (leftover == 1) {
+ return {BASE64_INPUT_REMAINDER, size_t(dst - dstinit)};
+ }
+ if (leftover == 2) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+ triple >>= 8;
+ std::memcpy(dst, &triple, 1);
+ dst += 1;
+ } else if (leftover == 3) {
+ uint32_t triple = (uint32_t(buffer_start[0]) << 3 * 6) +
+ (uint32_t(buffer_start[1]) << 2 * 6) +
+ (uint32_t(buffer_start[2]) << 1 * 6);
+ triple = scalar::utf32::swap_bytes(triple);
+
+ triple >>= 8;
+
+ std::memcpy(dst, &triple, 2);
+ dst += 2;
+ } else {
+ uint32_t triple = ((uint32_t(uint8_t(buffer_start[0])) << 3 * 6) +
+ (uint32_t(uint8_t(buffer_start[1])) << 2 * 6) +
+ (uint32_t(uint8_t(buffer_start[2])) << 1 * 6) +
+ (uint32_t(uint8_t(buffer_start[3])) << 0 * 6))
+ << 8;
+ triple = scalar::utf32::swap_bytes(triple);
+ std::memcpy(dst, &triple, 3);
+ dst += 3;
+ }
+ }
+ }
+ if (src < srcend + equalsigns) {
+ result r =
+ scalar::base64::base64_tail_decode(dst, src, srcend - src, options);
+ if (r.error == error_code::INVALID_BASE64_CHARACTER) {
+ r.count += size_t(src - srcinit);
+ return r;
+ } else {
+ r.count += size_t(dst - dstinit);
+ }
+ if(r.error == error_code::SUCCESS && equalsigns > 0) {
+ // additional checks
+ if((r.count % 3 == 0) || ((r.count % 3) + 1 + equalsigns != 4)) {
+ r.error = error_code::INVALID_BASE64_CHARACTER;
+ }
+ }
+ return r;
+ }
+ if(equalsigns > 0) {
+ if((size_t(dst - dstinit) % 3 == 0) || ((size_t(dst - dstinit) % 3) + 1 + equalsigns != 4)) {
+ return {INVALID_BASE64_CHARACTER, size_t(dst - dstinit)};
+ }
+ }
+ return {SUCCESS, size_t(dst - dstinit)};
+}
+/* end file src/westmere/sse_base64.cpp */
+
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+
+/* begin file src/generic/buf_block_reader.h */
+namespace simdutf {
+namespace westmere {
+namespace {
+
+// Walks through a buffer in block-sized increments, loading the last part with spaces
+template<size_t STEP_SIZE>
+struct buf_block_reader {
+public:
+ simdutf_really_inline buf_block_reader(const uint8_t *_buf, size_t _len);
+ simdutf_really_inline size_t block_index();
+ simdutf_really_inline bool has_full_block() const;
+ simdutf_really_inline const uint8_t *full_block() const;
+ /**
+ * Get the last block, padded with spaces.
+ *
+ * There will always be a last block, with at least 1 byte, unless len == 0 (in which case this
+ * function fills the buffer with spaces and returns 0. In particular, if len == STEP_SIZE there
+ * will be 0 full_blocks and 1 remainder block with STEP_SIZE bytes and no spaces for padding.
+ *
+ * @return the number of effective characters in the last block.
+ */
+ simdutf_really_inline size_t get_remainder(uint8_t *dst) const;
+ simdutf_really_inline void advance();
+private:
+ const uint8_t *buf;
+ const size_t len;
+ const size_t lenminusstep;
+ size_t idx;
+};
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text_64(const uint8_t *text) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ buf[i] = int8_t(text[i]) < ' ' ? '_' : int8_t(text[i]);
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+// Routines to print masks and text for debugging bitmask operations
+simdutf_unused static char * format_input_text(const simd8x64<uint8_t>& in) {
+ static char *buf = reinterpret_cast<char*>(malloc(sizeof(simd8x64<uint8_t>) + 1));
+ in.store(reinterpret_cast<uint8_t*>(buf));
+ for (size_t i=0; i<sizeof(simd8x64<uint8_t>); i++) {
+ if (buf[i] < ' ') { buf[i] = '_'; }
+ }
+ buf[sizeof(simd8x64<uint8_t>)] = '\0';
+ return buf;
+}
+
+simdutf_unused static char * format_mask(uint64_t mask) {
+ static char *buf = reinterpret_cast<char*>(malloc(64 + 1));
+ for (size_t i=0; i<64; i++) {
+ buf[i] = (mask & (size_t(1) << i)) ? 'X' : ' ';
+ }
+ buf[64] = '\0';
+ return buf;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline buf_block_reader<STEP_SIZE>::buf_block_reader(const uint8_t *_buf, size_t _len) : buf{_buf}, len{_len}, lenminusstep{len < STEP_SIZE ? 0 : len - STEP_SIZE}, idx{0} {}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::block_index() { return idx; }
+
+template<size_t STEP_SIZE>
+simdutf_really_inline bool buf_block_reader<STEP_SIZE>::has_full_block() const {
+ return idx < lenminusstep;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline const uint8_t *buf_block_reader<STEP_SIZE>::full_block() const {
+ return &buf[idx];
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline size_t buf_block_reader<STEP_SIZE>::get_remainder(uint8_t *dst) const {
+ if(len == idx) { return 0; } // memcpy(dst, null, 0) will trigger an error with some sanitizers
+ std::memset(dst, 0x20, STEP_SIZE); // std::memset STEP_SIZE because it's more efficient to write out 8 or 16 bytes at once.
+ std::memcpy(dst, buf + idx, len - idx);
+ return len - idx;
+}
+
+template<size_t STEP_SIZE>
+simdutf_really_inline void buf_block_reader<STEP_SIZE>::advance() {
+ idx += STEP_SIZE;
+}
+
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/buf_block_reader.h */
+/* begin file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_validation {
+
+using namespace simd;
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+ //
+ // Return nonzero if there are incomplete multibyte characters at the end of the block:
+ // e.g. if there is a 4-byte character, but it's 3 bytes from the end.
+ //
+ simdutf_really_inline simd8<uint8_t> is_incomplete(const simd8<uint8_t> input) {
+ // If the previous input's last 3 bytes match this, they're too short (they ended at EOF):
+ // ... 1111____ 111_____ 11______
+ static const uint8_t max_array[32] = {
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 255, 255, 255,
+ 255, 255, 255, 255, 255, 0b11110000u-1, 0b11100000u-1, 0b11000000u-1
+ };
+ const simd8<uint8_t> max_value(&max_array[sizeof(max_array)-sizeof(simd8<uint8_t>)]);
+ return input.gt_bits(max_value);
+ }
+
+ struct utf8_checker {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+ // The last input we received
+ simd8<uint8_t> prev_input_block;
+ // Whether the last input we received was incomplete (used for ASCII fast path)
+ simd8<uint8_t> prev_incomplete;
+
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+ // The only problem that can happen at EOF is that a multibyte character is too short
+ // or a byte value too large in the last bytes: check_special_cases only checks for bytes
+ // too large in the first of two bytes.
+ simdutf_really_inline void check_eof() {
+ // If the previous block had incomplete UTF-8 characters at the end, an ASCII block can't
+ // possibly finish them.
+ this->error |= this->prev_incomplete;
+ }
+
+ simdutf_really_inline void check_next_input(const simd8x64<uint8_t>& input) {
+ if(simdutf_likely(is_ascii(input))) {
+ this->error |= this->prev_incomplete;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], this->prev_input_block);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ this->prev_incomplete = is_incomplete(input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1]);
+ this->prev_input_block = input.chunks[simd8x64<uint8_t>::NUM_CHUNKS-1];
+
+ }
+ }
+
+ // do not forget to call check_eof!
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // namespace utf8_validation
+
+using utf8_validation::utf8_checker;
+
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_lookup4_algorithm.h */
+/* begin file src/generic/utf8_validation/utf8_validator.h */
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_validation {
+
+/**
+ * Validates that the string is actual UTF-8.
+ */
+template<class checker>
+bool generic_validate_utf8(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ return !c.errors();
+}
+
+bool generic_validate_utf8(const char * input, size_t length) {
+ return generic_validate_utf8<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+/**
+ * Validates that the string is actual UTF-8 and stops on errors.
+ */
+template<class checker>
+result generic_validate_utf8_with_errors(const uint8_t * input, size_t length) {
+ checker c{};
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ c.check_next_input(in);
+ if(c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input + count), length - count);
+ res.count += count;
+ return res;
+ }
+ reader.advance();
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ c.check_next_input(in);
+ reader.advance();
+ c.check_eof();
+ if (c.errors()) {
+ if (count != 0) { count--; } // Sometimes the error is only detected in the next chunk
+ result res = scalar::utf8::rewind_and_validate_with_errors(reinterpret_cast<const char*>(input), reinterpret_cast<const char*>(input) + count, length - count);
+ res.count += count;
+ return res;
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_utf8_with_errors(const char * input, size_t length) {
+ return generic_validate_utf8_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+bool generic_validate_ascii(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ uint8_t blocks[64]{};
+ simd::simd8x64<uint8_t> running_or(blocks);
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ running_or |= in;
+ reader.advance();
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ running_or |= in;
+ return running_or.is_ascii();
+}
+
+bool generic_validate_ascii(const char * input, size_t length) {
+ return generic_validate_ascii<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+template<class checker>
+result generic_validate_ascii_with_errors(const uint8_t * input, size_t length) {
+ buf_block_reader<64> reader(input, length);
+ size_t count{0};
+ while (reader.has_full_block()) {
+ simd::simd8x64<uint8_t> in(reader.full_block());
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ }
+ reader.advance();
+
+ count += 64;
+ }
+ uint8_t block[64]{};
+ reader.get_remainder(block);
+ simd::simd8x64<uint8_t> in(block);
+ if (!in.is_ascii()) {
+ result res = scalar::ascii::validate_with_errors(reinterpret_cast<const char*>(input + count), length - count);
+ return result(res.error, count + res.count);
+ } else {
+ return result(error_code::SUCCESS, length);
+ }
+}
+
+result generic_validate_ascii_with_errors(const char * input, size_t length) {
+ return generic_validate_ascii_with_errors<utf8_checker>(reinterpret_cast<const uint8_t *>(input),length);
+}
+
+} // namespace utf8_validation
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_validation/utf8_validator.h */
+// transcoding from UTF-8 to UTF-16
+/* begin file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_utf16 {
+
+using namespace simd;
+
+template <endianness endian>
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char16_t* utf16_output) noexcept {
+ // The implementation is not specific to haswell and should be moved to the generic directory.
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ // this loop could be unrolled further. For example, we could process the mask
+ // far more than 64 bytes.
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // Slow path. We hope that the compiler will recognize that this is a slow path.
+ // Anything that is not a continuation mask is a 'leading byte', that is, the
+ // start of a new code point.
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ // The *start* of code points is not so useful, rather, we want the *end* of code points.
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times when using solely
+ // the slow/regular path, and at least four times if there are fast paths.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ //
+ // Thus we may allow convert_masked_utf8_to_utf16 to process
+ // more bytes at a time under a fast-path mode where 16 bytes
+ // are consumed at once (e.g., when encountering ASCII).
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(input + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ utf16_output += scalar::utf8_to_utf16::convert_valid<endian>(input + pos, size - pos, utf16_output);
+ return utf16_output - start;
+}
+
+} // namespace utf8_to_utf16
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/valid_utf8_to_utf16.h */
+/* begin file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_utf16 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+ template <endianness endian>
+ simdutf_really_inline size_t convert(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf16::convert<endian>(in + pos, size - pos, utf16_output);
+ if(howmany == 0) { return 0; }
+ utf16_output += howmany;
+ }
+ return utf16_output - start;
+ }
+
+ template <endianness endian>
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char16_t* utf16_output) {
+ size_t pos = 0;
+ char16_t* start{utf16_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf16. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf16<endian>(utf16_output);
+ utf16_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf16<endian>(in + pos,
+ utf8_end_of_code_point_mask, utf16_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_utf16::rewind_and_convert_with_errors<endian>(pos, in + pos, size - pos, utf16_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf16_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf16_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf16 namespace
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf16/utf8_to_utf16.h */
+// transcoding from UTF-8 to UTF-32
+/* begin file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_utf32 {
+
+using namespace simd;
+
+
+simdutf_warn_unused size_t convert_valid(const char* input, size_t size,
+ char32_t* utf32_output) noexcept {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ const size_t safety_margin = 16; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> in(reinterpret_cast<const int8_t *>(input + pos));
+ if(in.is_ascii()) {
+ in.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // -65 is 0b10111111 in two-complement's, so largest possible continuation byte
+ uint64_t utf8_continuation_mask = in.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ size_t max_starting_point = (pos + 64) - 12;
+ while(pos < max_starting_point) {
+ size_t consumed = convert_masked_utf8_to_utf32(input + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ }
+ }
+ utf32_output += scalar::utf8_to_utf32::convert_valid(input + pos, size - pos, utf32_output);
+ return utf32_output - start;
+}
+
+
+} // namespace utf8_to_utf32
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/valid_utf8_to_utf32.h */
+/* begin file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_utf32 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ TOO_SHORT,
+ // 1110____ ________ <three byte lead in byte 1>
+ TOO_SHORT | OVERLONG_3 | SURROGATE,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ TOO_SHORT | TOO_LARGE | TOO_LARGE_1000 | OVERLONG_4
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ CARRY | TOO_LARGE,
+ // ____0101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____011_ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+
+ // ____1___ ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ // ____1101 ________
+ CARRY | TOO_LARGE | TOO_LARGE_1000 | SURROGATE,
+ CARRY | TOO_LARGE | TOO_LARGE_1000,
+ CARRY | TOO_LARGE | TOO_LARGE_1000
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+ simdutf_really_inline simd8<uint8_t> check_multibyte_lengths(const simd8<uint8_t> input,
+ const simd8<uint8_t> prev_input, const simd8<uint8_t> sc) {
+ simd8<uint8_t> prev2 = input.prev<2>(prev_input);
+ simd8<uint8_t> prev3 = input.prev<3>(prev_input);
+ simd8<uint8_t> must23 = simd8<uint8_t>(must_be_2_3_continuation(prev2, prev3));
+ simd8<uint8_t> must23_80 = must23 & uint8_t(0x80);
+ return must23_80 ^ sc;
+ }
+
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ simd8<uint8_t> sc = check_special_cases(input, prev1);
+ this->error |= check_multibyte_lengths(input, prev_input, sc);
+ }
+
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_utf32::convert(in + pos, size - pos, utf32_output);
+ if(howmany == 0) { return 0; }
+ utf32_output += howmany;
+ }
+ return utf32_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char32_t* utf32_output) {
+ size_t pos = 0;
+ char32_t* start{utf32_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_utf32. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the fourth last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store_ascii_as_utf32(utf32_output);
+ utf32_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_utf32(in + pos,
+ utf8_end_of_code_point_mask, utf32_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ result res = scalar::utf8_to_utf32::rewind_and_convert_with_errors(pos, in + pos, size - pos, utf32_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ utf32_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, utf32_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_utf32 namespace
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_to_utf32/utf8_to_utf32.h */
+// other functions
+/* begin file src/generic/utf8.h */
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8 {
+
+using namespace simd;
+
+simdutf_really_inline size_t count_code_points(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.gt(-65);
+ count += count_ones(utf8_continuation_mask);
+ }
+ return count + scalar::utf8::count_code_points(in + pos, size - pos);
+}
+
+simdutf_really_inline size_t utf16_length_from_utf8(const char* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos + 64 <= size; pos += 64) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ // We count one word for anything that is not a continuation (so
+ // leading bytes).
+ count += 64 - count_ones(utf8_continuation_mask);
+ int64_t utf8_4byte = input.gteq_unsigned(240);
+ count += count_ones(utf8_4byte);
+ }
+ return count + scalar::utf8::utf16_length_from_utf8(in + pos, size - pos);
+}
+} // utf8 namespace
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8.h */
+/* begin file src/generic/utf16.h */
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf16 {
+
+template <endianness big_endian>
+simdutf_really_inline size_t count_code_points(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t not_pair = input.not_in_range(0xDC00, 0xDFFF);
+ count += count_ones(not_pair) / 2;
+ }
+ return count + scalar::utf16::count_code_points<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf8_length_from_utf16(const char16_t* in, size_t size) {
+ size_t pos = 0;
+ size_t count = 0;
+ // This algorithm could no doubt be improved!
+ for(;pos < size/32*32; pos += 32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ if (!match_system(big_endian)) { input.swap_bytes(); }
+ uint64_t ascii_mask = input.lteq(0x7F);
+ uint64_t twobyte_mask = input.lteq(0x7FF);
+ uint64_t not_pair_mask = input.not_in_range(0xD800, 0xDFFF);
+
+ size_t ascii_count = count_ones(ascii_mask) / 2;
+ size_t twobyte_count = count_ones(twobyte_mask & ~ ascii_mask) / 2;
+ size_t threebyte_count = count_ones(not_pair_mask & ~ twobyte_mask) / 2;
+ size_t fourbyte_count = 32 - count_ones(not_pair_mask) / 2;
+ count += 2 * fourbyte_count + 3 * threebyte_count + 2 * twobyte_count + ascii_count;
+ }
+ return count + scalar::utf16::utf8_length_from_utf16<big_endian>(in + pos, size - pos);
+}
+
+template <endianness big_endian>
+simdutf_really_inline size_t utf32_length_from_utf16(const char16_t* in, size_t size) {
+ return count_code_points<big_endian>(in, size);
+}
+
+simdutf_really_inline void change_endianness_utf16(const char16_t* in, size_t size, char16_t* output) {
+ size_t pos = 0;
+
+ while (pos < size/32*32) {
+ simd16x32<uint16_t> input(reinterpret_cast<const uint16_t *>(in + pos));
+ input.swap_bytes();
+ input.store(reinterpret_cast<uint16_t *>(output));
+ pos += 32;
+ output += 32;
+ }
+
+ scalar::utf16::change_endianness_utf16(in + pos, size - pos, output);
+}
+
+} // utf16
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf16.h */
+// transcoding from UTF-8 to Latin 1
+/* begin file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline simd8<uint8_t> check_special_cases(const simd8<uint8_t> input, const simd8<uint8_t> prev1) {
+// For UTF-8 to Latin 1, we can allow any ASCII character, and any continuation byte,
+// but the non-ASCII leading bytes must be 0b11000011 or 0b11000010 and nothing else.
+//
+// Bit 0 = Too Short (lead byte/ASCII followed by lead byte/ASCII)
+// Bit 1 = Too Long (ASCII followed by continuation)
+// Bit 2 = Overlong 3-byte
+// Bit 4 = Surrogate
+// Bit 5 = Overlong 2-byte
+// Bit 7 = Two Continuations
+ constexpr const uint8_t TOO_SHORT = 1<<0; // 11______ 0_______
+ // 11______ 11______
+ constexpr const uint8_t TOO_LONG = 1<<1; // 0_______ 10______
+ constexpr const uint8_t OVERLONG_3 = 1<<2; // 11100000 100_____
+ constexpr const uint8_t SURROGATE = 1<<4; // 11101101 101_____
+ constexpr const uint8_t OVERLONG_2 = 1<<5; // 1100000_ 10______
+ constexpr const uint8_t TWO_CONTS = 1<<7; // 10______ 10______
+ constexpr const uint8_t TOO_LARGE = 1<<3; // 11110100 1001____
+ // 11110100 101_____
+ // 11110101 1001____
+ // 11110101 101_____
+ // 1111011_ 1001____
+ // 1111011_ 101_____
+ // 11111___ 1001____
+ // 11111___ 101_____
+ constexpr const uint8_t TOO_LARGE_1000 = 1<<6;
+ // 11110101 1000____
+ // 1111011_ 1000____
+ // 11111___ 1000____
+ constexpr const uint8_t OVERLONG_4 = 1<<6; // 11110000 1000____
+ constexpr const uint8_t FORBIDDEN = 0xff;
+
+ const simd8<uint8_t> byte_1_high = prev1.shr<4>().lookup_16<uint8_t>(
+ // 0_______ ________ <ASCII in byte 1>
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ TOO_LONG, TOO_LONG, TOO_LONG, TOO_LONG,
+ // 10______ ________ <continuation in byte 1>
+ TWO_CONTS, TWO_CONTS, TWO_CONTS, TWO_CONTS,
+ // 1100____ ________ <two byte lead in byte 1>
+ TOO_SHORT | OVERLONG_2,
+ // 1101____ ________ <two byte lead in byte 1>
+ FORBIDDEN,
+ // 1110____ ________ <three byte lead in byte 1>
+ FORBIDDEN,
+ // 1111____ ________ <four+ byte lead in byte 1>
+ FORBIDDEN
+ );
+ constexpr const uint8_t CARRY = TOO_SHORT | TOO_LONG | TWO_CONTS; // These all have ____ in byte 1 .
+ const simd8<uint8_t> byte_1_low = (prev1 & 0x0F).lookup_16<uint8_t>(
+ // ____0000 ________
+ CARRY | OVERLONG_3 | OVERLONG_2 | OVERLONG_4,
+ // ____0001 ________
+ CARRY | OVERLONG_2,
+ // ____001_ ________
+ CARRY,
+ CARRY,
+
+ // ____0100 ________
+ FORBIDDEN,
+ // ____0101 ________
+ FORBIDDEN,
+ // ____011_ ________
+ FORBIDDEN,
+ FORBIDDEN,
+
+ // ____1___ ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN,
+ // ____1101 ________
+ FORBIDDEN,
+ FORBIDDEN,
+ FORBIDDEN
+ );
+ const simd8<uint8_t> byte_2_high = input.shr<4>().lookup_16<uint8_t>(
+ // ________ 0_______ <ASCII in byte 2>
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT,
+
+ // ________ 1000____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE_1000 | OVERLONG_4,
+ // ________ 1001____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | OVERLONG_3 | TOO_LARGE,
+ // ________ 101_____
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+ TOO_LONG | OVERLONG_2 | TWO_CONTS | SURROGATE | TOO_LARGE,
+
+ // ________ 11______
+ TOO_SHORT, TOO_SHORT, TOO_SHORT, TOO_SHORT
+ );
+ return (byte_1_high & byte_1_low & byte_2_high);
+ }
+
+ struct validating_transcoder {
+ // If this is nonzero, there has been a UTF-8 error.
+ simd8<uint8_t> error;
+
+ validating_transcoder() : error(uint8_t(0)) {}
+ //
+ // Check whether the current bytes are valid UTF-8.
+ //
+ simdutf_really_inline void check_utf8_bytes(const simd8<uint8_t> input, const simd8<uint8_t> prev_input) {
+ // Flip prev1...prev3 so we can easily determine if they are 2+, 3+ or 4+ lead bytes
+ // (2, 3, 4-byte leads become large positive numbers instead of small negative numbers)
+ simd8<uint8_t> prev1 = input.prev<1>(prev_input);
+ this->error |= check_special_cases(input, prev1);
+ }
+
+
+ simdutf_really_inline size_t convert(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) { return 0; }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert(in + pos, size - pos, latin1_output);
+ if(howmany == 0) { return 0; }
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ simdutf_really_inline result convert_with_errors(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65);
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ static_assert((simd8x64<uint8_t>::NUM_CHUNKS == 2) || (simd8x64<uint8_t>::NUM_CHUNKS == 4),
+ "We support either two or four chunks per 64-byte block.");
+ auto zero = simd8<uint8_t>{uint8_t(0)};
+ if(simd8x64<uint8_t>::NUM_CHUNKS == 2) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ } else if(simd8x64<uint8_t>::NUM_CHUNKS == 4) {
+ this->check_utf8_bytes(input.chunks[0], zero);
+ this->check_utf8_bytes(input.chunks[1], input.chunks[0]);
+ this->check_utf8_bytes(input.chunks[2], input.chunks[1]);
+ this->check_utf8_bytes(input.chunks[3], input.chunks[2]);
+ }
+ if (errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1);
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(errors()) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ res.count += pos;
+ return res;
+ }
+ if(pos < size) {
+ // rewind_and_convert_with_errors will seek a potential error from in+pos onward,
+ // with the ability to go back up to pos bytes, and read size-pos bytes forward.
+ result res = scalar::utf8_to_latin1::rewind_and_convert_with_errors(pos, in + pos, size - pos, latin1_output);
+ if (res.error) { // In case of error, we want the error position
+ res.count += pos;
+ return res;
+ } else { // In case of success, we want the number of word written
+ latin1_output += res.count;
+ }
+ }
+ return result(error_code::SUCCESS, latin1_output - start);
+ }
+
+ simdutf_really_inline bool errors() const {
+ return this->error.any_bits_set_anywhere();
+ }
+
+ }; // struct utf8_checker
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace westmere
+} // namespace simdutf
+/* end file src/generic/utf8_to_latin1/utf8_to_latin1.h */
+/* begin file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+
+namespace simdutf {
+namespace westmere {
+namespace {
+namespace utf8_to_latin1 {
+using namespace simd;
+
+
+ simdutf_really_inline size_t convert_valid(const char* in, size_t size, char* latin1_output) {
+ size_t pos = 0;
+ char* start{latin1_output};
+ // In the worst case, we have the haswell kernel which can cause an overflow of
+ // 8 bytes when calling convert_masked_utf8_to_latin1. If you skip the last 16 bytes,
+ // and if the data is valid, then it is entirely safe because 16 UTF-8 bytes generate
+ // much more than 8 bytes. However, you cannot generally assume that you have valid
+ // UTF-8 input, so we are going to go back from the end counting 8 leading bytes,
+ // to give us a good margin.
+ size_t leading_byte = 0;
+ size_t margin = size;
+ for(; margin > 0 && leading_byte < 8; margin--) {
+ leading_byte += (int8_t(in[margin-1]) > -65); //twos complement of -65 is 1011 1111 ...
+ }
+ // If the input is long enough, then we have that margin-1 is the eight last leading byte.
+ const size_t safety_margin = size - margin + 1; // to avoid overruns!
+ while(pos + 64 + safety_margin <= size) {
+ simd8x64<int8_t> input(reinterpret_cast<const int8_t *>(in + pos));
+ if(input.is_ascii()) {
+ input.store((int8_t*)latin1_output);
+ latin1_output += 64;
+ pos += 64;
+ } else {
+ // you might think that a for-loop would work, but under Visual Studio, it is not good enough.
+ uint64_t utf8_continuation_mask = input.lt(-65 + 1); // -64 is 1100 0000 in twos complement. Note: in this case, we also have ASCII to account for.
+ uint64_t utf8_leading_mask = ~utf8_continuation_mask;
+ uint64_t utf8_end_of_code_point_mask = utf8_leading_mask>>1;
+ // We process in blocks of up to 12 bytes except possibly
+ // for fast paths which may process up to 16 bytes. For the
+ // slow path to work, we should have at least 12 input bytes left.
+ size_t max_starting_point = (pos + 64) - 12;
+ // Next loop is going to run at least five times.
+ while(pos < max_starting_point) {
+ // Performance note: our ability to compute 'consumed' and
+ // then shift and recompute is critical. If there is a
+ // latency of, say, 4 cycles on getting 'consumed', then
+ // the inner loop might have a total latency of about 6 cycles.
+ // Yet we process between 6 to 12 inputs bytes, thus we get
+ // a speed limit between 1 cycle/byte and 0.5 cycle/byte
+ // for this section of the code. Hence, there is a limit
+ // to how much we can further increase this latency before
+ // it seriously harms performance.
+ size_t consumed = convert_masked_utf8_to_latin1(in + pos,
+ utf8_end_of_code_point_mask, latin1_output);
+ pos += consumed;
+ utf8_end_of_code_point_mask >>= consumed;
+ }
+ // At this point there may remain between 0 and 12 bytes in the
+ // 64-byte block. These bytes will be processed again. So we have an
+ // 80% efficiency (in the worst case). In practice we expect an
+ // 85% to 90% efficiency.
+ }
+ }
+ if(pos < size) {
+ size_t howmany = scalar::utf8_to_latin1::convert_valid(in + pos, size - pos, latin1_output);
+ latin1_output += howmany;
+ }
+ return latin1_output - start;
+ }
+
+ }
+} // utf8_to_latin1 namespace
+} // unnamed namespace
+} // namespace westmere
+ // namespace simdutf
+/* end file src/generic/utf8_to_latin1/valid_utf8_to_latin1.h */
+
+
+//
+// Implementation-specific overrides
+//
+
+namespace simdutf {
+namespace westmere {
+
+simdutf_warn_unused int implementation::detect_encodings(const char * input, size_t length) const noexcept {
+ // If there is a BOM, then we trust it.
+ auto bom_encoding = simdutf::BOM::check_bom(input, length);
+ if(bom_encoding != encoding_type::unspecified) { return bom_encoding; }
+ if (length % 2 == 0) {
+ return sse_detect_encodings<utf8_validation::utf8_checker>(input, length);
+ } else {
+ if (implementation::validate_utf8(input, length)) {
+ return simdutf::encoding_type::UTF8;
+ } else {
+ return simdutf::encoding_type::unspecified;
+ }
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf8(const char *buf, size_t len) const noexcept {
+ return westmere::utf8_validation::generic_validate_utf8(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_utf8_with_errors(const char *buf, size_t len) const noexcept {
+ return westmere::utf8_validation::generic_validate_utf8_with_errors(buf, len);
+}
+
+simdutf_warn_unused bool implementation::validate_ascii(const char *buf, size_t len) const noexcept {
+ return westmere::utf8_validation::generic_validate_ascii(buf, len);
+}
+
+simdutf_warn_unused result implementation::validate_ascii_with_errors(const char *buf, size_t len) const noexcept {
+ return westmere::utf8_validation::generic_validate_ascii_with_errors(buf,len);
+}
+
+simdutf_warn_unused bool implementation::validate_utf16le(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = sse_validate_utf16<endianness::LITTLE>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::LITTLE>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf16be(const char16_t *buf, size_t len) const noexcept {
+ const char16_t* tail = sse_validate_utf16<endianness::BIG>(buf, len);
+ if (tail) {
+ return scalar::utf16::validate<endianness::BIG>(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = sse_validate_utf16_with_errors<endianness::LITTLE>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::LITTLE>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept {
+ result res = sse_validate_utf16_with_errors<endianness::BIG>(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf16::validate_with_errors<endianness::BIG>(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused bool implementation::validate_utf32(const char32_t *buf, size_t len) const noexcept {
+ const char32_t* tail = sse_validate_utf32le(buf, len);
+ if (tail) {
+ return scalar::utf32::validate(tail, len - (tail - buf));
+ } else {
+ return false;
+ }
+}
+
+simdutf_warn_unused result implementation::validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept {
+ result res = sse_validate_utf32le_with_errors(buf, len);
+ if (res.count != len) {
+ result scalar_res = scalar::utf32::validate_with_errors(buf + res.count, len - res.count);
+ return result(scalar_res.error, res.count + scalar_res.count);
+ } else {
+ return res;
+ }
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf8(const char * buf, size_t len, char* utf8_output) const noexcept {
+
+ std::pair<const char*, char*> ret = sse_convert_latin1_to_utf8(buf, len, utf8_output);
+ size_t converted_chars = ret.second - utf8_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ converted_chars += scalar_converted_chars;
+ }
+
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = sse_convert_latin1_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char*, char16_t*> ret = sse_convert_latin1_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+simdutf_warn_unused size_t implementation::convert_latin1_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char*, char32_t*> ret = sse_convert_latin1_to_utf32(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t converted_chars = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_converted_chars = scalar::latin1_to_utf32::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_converted_chars == 0) { return 0; }
+ converted_chars += scalar_converted_chars;
+ }
+ return converted_chars;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert(buf, len, latin1_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_latin1_with_errors(const char* buf, size_t len, char* latin1_output) const noexcept {
+ utf8_to_latin1::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_latin1(const char* buf, size_t len, char* latin1_output) const noexcept {
+ return westmere::utf8_to_latin1::convert_valid(buf,len,latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16le(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf16be(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert<endianness::BIG>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16le_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf16be_with_errors(const char* buf, size_t len, char16_t* utf16_output) const noexcept {
+ utf8_to_utf16::validating_transcoder converter;
+ return converter.convert_with_errors<endianness::BIG>(buf, len, utf16_output);
+}
+
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16le(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::LITTLE>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf16be(const char* input, size_t size,
+ char16_t* utf16_output) const noexcept {
+ return utf8_to_utf16::convert_valid<endianness::BIG>(input, size, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf8_to_utf32(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert(buf, len, utf32_output);
+}
+
+simdutf_warn_unused result implementation::convert_utf8_to_utf32_with_errors(const char* buf, size_t len, char32_t* utf32_output) const noexcept {
+ utf8_to_utf32::validating_transcoder converter;
+ return converter.convert_with_errors(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf8_to_utf32(const char* input, size_t size,
+ char32_t* utf32_output) const noexcept {
+ return utf8_to_utf32::convert_valid(input, size, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = sse_convert_utf16_to_latin1<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = sse_convert_utf16_to_latin1<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_latin1::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = sse_convert_utf16_to_latin1_with_errors<endianness::LITTLE>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_latin1_with_errors(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<result, char*> ret = sse_convert_utf16_to_latin1_with_errors<endianness::BIG>(buf, len, latin1_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_latin1::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: we could provide an optimized function.
+ return convert_utf16be_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_latin1(const char16_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: we could provide an optimized function.
+ return convert_utf16le_to_latin1(buf, len, latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = sse_convert_utf16_to_utf8<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char16_t*, char*> ret = sse_convert_utf16_to_utf8<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf8::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = westmere::sse_convert_utf16_to_utf8_with_errors<endianness::LITTLE>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf8_with_errors(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = westmere::sse_convert_utf16_to_utf8_with_errors<endianness::BIG>(buf, len, utf8_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf8::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16le_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf8(const char16_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf16be_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = sse_convert_utf32_to_latin1(buf, len, latin1_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - latin1_output;
+ // if (ret.first != buf + len) {
+ if (ret.first < buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_latin1::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+
+simdutf_warn_unused result implementation::convert_utf32_to_latin1_with_errors(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = westmere::sse_convert_utf32_to_latin1_with_errors(buf, len, latin1_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_latin1::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - latin1_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_latin1(const char32_t* buf, size_t len, char* latin1_output) const noexcept {
+ // optimization opportunity: we could provide an optimized function.
+ return convert_utf32_to_latin1(buf,len,latin1_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ std::pair<const char32_t*, char*> ret = sse_convert_utf32_to_utf8(buf, len, utf8_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf8_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf8::convert(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf8_with_errors(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char*> ret = westmere::sse_convert_utf32_to_utf8_with_errors(buf, len, utf8_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf8::convert_with_errors(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf8_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = sse_convert_utf16_to_utf32<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ std::pair<const char16_t*, char32_t*> ret = sse_convert_utf16_to_utf32<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf32_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf16_to_utf32::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf16le_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = westmere::sse_convert_utf16_to_utf32_with_errors<endianness::LITTLE>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf16be_to_utf32_with_errors(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char32_t*> ret = westmere::sse_convert_utf16_to_utf32_with_errors<endianness::BIG>(buf, len, utf32_output);
+ if (ret.first.error) { return ret.first; } // Can return directly since scalar fallback already found correct ret.first.count
+ if (ret.first.count != len) { // All good so far, but not finished
+ result scalar_res = scalar::utf16_to_utf32::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf32_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf8(const char32_t* buf, size_t len, char* utf8_output) const noexcept {
+ return convert_utf32_to_utf8(buf, len, utf8_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = sse_convert_utf32_to_utf16<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::LITTLE>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused size_t implementation::convert_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ std::pair<const char32_t*, char16_t*> ret = sse_convert_utf32_to_utf16<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first == nullptr) { return 0; }
+ size_t saved_bytes = ret.second - utf16_output;
+ if (ret.first != buf + len) {
+ const size_t scalar_saved_bytes = scalar::utf32_to_utf16::convert<endianness::BIG>(
+ ret.first, len - (ret.first - buf), ret.second);
+ if (scalar_saved_bytes == 0) { return 0; }
+ saved_bytes += scalar_saved_bytes;
+ }
+ return saved_bytes;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16le_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = westmere::sse_convert_utf32_to_utf16_with_errors<endianness::LITTLE>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::LITTLE>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused result implementation::convert_utf32_to_utf16be_with_errors(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ // ret.first.count is always the position in the buffer, not the number of code units written even if finished
+ std::pair<result, char16_t*> ret = westmere::sse_convert_utf32_to_utf16_with_errors<endianness::BIG>(buf, len, utf16_output);
+ if (ret.first.count != len) {
+ result scalar_res = scalar::utf32_to_utf16::convert_with_errors<endianness::BIG>(
+ buf + ret.first.count, len - ret.first.count, ret.second);
+ if (scalar_res.error) {
+ scalar_res.count += ret.first.count;
+ return scalar_res;
+ } else {
+ ret.second += scalar_res.count;
+ }
+ }
+ ret.first.count = ret.second - utf16_output; // Set count to the number of 8-bit code units written
+ return ret.first;
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16le(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16le(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf32_to_utf16be(const char32_t* buf, size_t len, char16_t* utf16_output) const noexcept {
+ return convert_utf32_to_utf16be(buf, len, utf16_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16le_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16le_to_utf32(buf, len, utf32_output);
+}
+
+simdutf_warn_unused size_t implementation::convert_valid_utf16be_to_utf32(const char16_t* buf, size_t len, char32_t* utf32_output) const noexcept {
+ return convert_utf16be_to_utf32(buf, len, utf32_output);
+}
+
+void implementation::change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept {
+ utf16::change_endianness_utf16(input, length, output);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::count_code_points<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::count_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf8(const char* buf, size_t len) const noexcept {
+ return count_utf8(buf,len);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf16(size_t length) const noexcept {
+ return scalar::utf16::latin1_length_from_utf16(length);
+}
+
+simdutf_warn_unused size_t implementation::latin1_length_from_utf32(size_t length) const noexcept {
+ return scalar::utf32::latin1_length_from_utf32(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf8_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf16_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_latin1(size_t length) const noexcept {
+ return scalar::latin1::utf32_length_from_latin1(length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_latin1(const char * input, size_t len) const noexcept {
+ const uint8_t *str = reinterpret_cast<const uint8_t *>(input);
+ size_t answer = len / sizeof(__m128i) * sizeof(__m128i);
+ size_t i = 0;
+ __m128i two_64bits = _mm_setzero_si128();
+ while (i + sizeof(__m128i) <= len) {
+ __m128i runner = _mm_setzero_si128();
+ size_t iterations = (len - i) / sizeof(__m128i);
+ if (iterations > 255) {
+ iterations = 255;
+ }
+ size_t max_i = i + iterations * sizeof(__m128i) - sizeof(__m128i);
+ for (; i + 4*sizeof(__m128i) <= max_i; i += 4*sizeof(__m128i)) {
+ __m128i input1 = _mm_loadu_si128((const __m128i *)(str + i));
+ __m128i input2 = _mm_loadu_si128((const __m128i *)(str + i + sizeof(__m128i)));
+ __m128i input3 = _mm_loadu_si128((const __m128i *)(str + i + 2*sizeof(__m128i)));
+ __m128i input4 = _mm_loadu_si128((const __m128i *)(str + i + 3*sizeof(__m128i)));
+ __m128i input12 = _mm_add_epi8(
+ _mm_cmpgt_epi8(
+ _mm_setzero_si128(),
+ input1),
+ _mm_cmpgt_epi8(
+ _mm_setzero_si128(),
+ input2));
+ __m128i input34 = _mm_add_epi8(
+ _mm_cmpgt_epi8(
+ _mm_setzero_si128(),
+ input3),
+ _mm_cmpgt_epi8(
+ _mm_setzero_si128(),
+ input4));
+ __m128i input1234 = _mm_add_epi8(input12, input34);
+ runner = _mm_sub_epi8(runner, input1234);
+ }
+ for (; i <= max_i; i += sizeof(__m128i)) {
+ __m128i more_input = _mm_loadu_si128((const __m128i *)(str + i));
+ runner = _mm_sub_epi8(
+ runner, _mm_cmpgt_epi8(_mm_setzero_si128(), more_input));
+ }
+ two_64bits = _mm_add_epi64(
+ two_64bits, _mm_sad_epu8(runner, _mm_setzero_si128()));
+ }
+ answer += _mm_extract_epi64(two_64bits, 0) +
+ _mm_extract_epi64(two_64bits, 1);
+ return answer + scalar::latin1::utf8_length_from_latin1(reinterpret_cast<const char *>(str + i), len - i);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::LITTLE>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept {
+ return utf16::utf32_length_from_utf16<endianness::BIG>(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::utf16_length_from_utf8(input, length);
+}
+
+simdutf_warn_unused size_t implementation::utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const __m128i v_00000000 = _mm_setzero_si128();
+ const __m128i v_ffffff80 = _mm_set1_epi32((uint32_t)0xffffff80);
+ const __m128i v_fffff800 = _mm_set1_epi32((uint32_t)0xfffff800);
+ const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 4 <= length; pos += 4) {
+ __m128i in = _mm_loadu_si128((__m128i*)(input + pos));
+ const __m128i ascii_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffffff80), v_00000000);
+ const __m128i one_two_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_fffff800), v_00000000);
+ const __m128i two_bytes_bytemask = _mm_xor_si128(one_two_bytes_bytemask, ascii_bytes_bytemask);
+ const __m128i one_two_three_bytes_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000);
+ const __m128i three_bytes_bytemask = _mm_xor_si128(one_two_three_bytes_bytemask, one_two_bytes_bytemask);
+ const uint16_t ascii_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(ascii_bytes_bytemask));
+ const uint16_t two_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(two_bytes_bytemask));
+ const uint16_t three_bytes_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(three_bytes_bytemask));
+
+ size_t ascii_count = count_ones(ascii_bytes_bitmask) / 4;
+ size_t two_bytes_count = count_ones(two_bytes_bitmask) / 4;
+ size_t three_bytes_count = count_ones(three_bytes_bitmask) / 4;
+ count += 16 - 3*ascii_count - 2*two_bytes_count - three_bytes_count;
+ }
+ return count + scalar::utf32::utf8_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept {
+ const __m128i v_00000000 = _mm_setzero_si128();
+ const __m128i v_ffff0000 = _mm_set1_epi32((uint32_t)0xffff0000);
+ size_t pos = 0;
+ size_t count = 0;
+ for(;pos + 4 <= length; pos += 4) {
+ __m128i in = _mm_loadu_si128((__m128i*)(input + pos));
+ const __m128i surrogate_bytemask = _mm_cmpeq_epi32(_mm_and_si128(in, v_ffff0000), v_00000000);
+ const uint16_t surrogate_bitmask = static_cast<uint16_t>(_mm_movemask_epi8(surrogate_bytemask));
+ size_t surrogate_count = (16-count_ones(surrogate_bitmask))/4;
+ count += 4 + surrogate_count;
+ }
+ return count + scalar::utf32::utf16_length_from_utf32(input + pos, length - pos);
+}
+
+simdutf_warn_unused size_t implementation::utf32_length_from_utf8(const char * input, size_t length) const noexcept {
+ return utf8::count_code_points(input, length);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept {
+ return scalar::base64::maximal_binary_length_from_base64(input, length);
+}
+
+simdutf_warn_unused result implementation::base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options) const noexcept {
+ return (options & base64_url) ? compress_decode_base64<true>(output, input, length, options) : compress_decode_base64<false>(output, input, length, options);
+}
+
+simdutf_warn_unused size_t implementation::base64_length_from_binary(size_t length, base64_options options) const noexcept {
+ return scalar::base64::base64_length_from_binary(length, options);
+}
+
+size_t implementation::binary_to_base64(const char * input, size_t length, char* output, base64_options options) const noexcept {
+ if(options == base64_url) {
+ return encode_base64<base64_url>(output, input, length);
+ } else {
+ return encode_base64<base64_default>(output, input, length);
+ }
+}
+} // namespace westmere
+} // namespace simdutf
+
+/* begin file src/simdutf/westmere/end.h */
+#if SIMDUTF_CAN_ALWAYS_RUN_WESTMERE
+// nothing needed.
+#else
+SIMDUTF_UNTARGET_REGION
+#endif
+
+/* end file src/simdutf/westmere/end.h */
+/* end file src/westmere/implementation.cpp */
+#endif
+
+SIMDUTF_POP_DISABLE_WARNINGS
+/* end file src/simdutf.cpp */
diff --git a/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.h b/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.h
new file mode 100644
index 00000000..d37bd2c7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/simdutf/vendor/simdutf.h
@@ -0,0 +1,3757 @@
+/* auto-generated on 2024-05-07 22:33:11 -0400. Do not edit! */
+/* begin file include/simdutf.h */
+#ifndef SIMDUTF_H
+#define SIMDUTF_H
+#include <cstring>
+
+/* begin file include/simdutf/compiler_check.h */
+#ifndef SIMDUTF_COMPILER_CHECK_H
+#define SIMDUTF_COMPILER_CHECK_H
+
+#ifndef __cplusplus
+#error simdutf requires a C++ compiler
+#endif
+
+#ifndef SIMDUTF_CPLUSPLUS
+#if defined(_MSVC_LANG) && !defined(__clang__)
+#define SIMDUTF_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG)
+#else
+#define SIMDUTF_CPLUSPLUS __cplusplus
+#endif
+#endif
+
+// C++ 17
+#if !defined(SIMDUTF_CPLUSPLUS17) && (SIMDUTF_CPLUSPLUS >= 201703L)
+#define SIMDUTF_CPLUSPLUS17 1
+#endif
+
+// C++ 14
+#if !defined(SIMDUTF_CPLUSPLUS14) && (SIMDUTF_CPLUSPLUS >= 201402L)
+#define SIMDUTF_CPLUSPLUS14 1
+#endif
+
+// C++ 11
+#if !defined(SIMDUTF_CPLUSPLUS11) && (SIMDUTF_CPLUSPLUS >= 201103L)
+#define SIMDUTF_CPLUSPLUS11 1
+#endif
+
+#ifndef SIMDUTF_CPLUSPLUS11
+#error simdutf requires a compiler compliant with the C++11 standard
+#endif
+
+#endif // SIMDUTF_COMPILER_CHECK_H
+/* end file include/simdutf/compiler_check.h */
+/* begin file include/simdutf/common_defs.h */
+#ifndef SIMDUTF_COMMON_DEFS_H
+#define SIMDUTF_COMMON_DEFS_H
+
+#include <cassert>
+/* begin file include/simdutf/portability.h */
+#ifndef SIMDUTF_PORTABILITY_H
+#define SIMDUTF_PORTABILITY_H
+
+#include <cstddef>
+#include <cstdint>
+#include <cstdlib>
+#include <cfloat>
+#include <cassert>
+#ifndef _WIN32
+// strcasecmp, strncasecmp
+#include <strings.h>
+#endif
+
+/**
+ * We want to check that it is actually a little endian system at
+ * compile-time.
+ */
+
+#if defined(__BYTE_ORDER__) && defined(__ORDER_BIG_ENDIAN__)
+#define SIMDUTF_IS_BIG_ENDIAN (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+#elif defined(_WIN32)
+#define SIMDUTF_IS_BIG_ENDIAN 0
+#else
+#if defined(__APPLE__) || defined(__FreeBSD__) // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
+#include <machine/endian.h>
+#elif defined(sun) || defined(__sun) // defined(__APPLE__) || defined(__FreeBSD__)
+#include <sys/byteorder.h>
+#else // defined(__APPLE__) || defined(__FreeBSD__)
+
+#ifdef __has_include
+#if __has_include(<endian.h>)
+#include <endian.h>
+#endif //__has_include(<endian.h>)
+#endif //__has_include
+
+#endif // defined(__APPLE__) || defined(__FreeBSD__)
+
+
+#ifndef !defined(__BYTE_ORDER__) || !defined(__ORDER_LITTLE_ENDIAN__)
+#define SIMDUTF_IS_BIG_ENDIAN 0
+#endif
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define SIMDUTF_IS_BIG_ENDIAN 0
+#else // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+#define SIMDUTF_IS_BIG_ENDIAN 1
+#endif // __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+
+#endif // defined __BYTE_ORDER__ && defined __ORDER_BIG_ENDIAN__
+
+
+/**
+ * At this point in time, SIMDUTF_IS_BIG_ENDIAN is defined.
+ */
+
+#ifdef _MSC_VER
+#define SIMDUTF_VISUAL_STUDIO 1
+/**
+ * We want to differentiate carefully between
+ * clang under visual studio and regular visual
+ * studio.
+ *
+ * Under clang for Windows, we enable:
+ * * target pragmas so that part and only part of the
+ * code gets compiled for advanced instructions.
+ *
+ */
+#ifdef __clang__
+// clang under visual studio
+#define SIMDUTF_CLANG_VISUAL_STUDIO 1
+#else
+// just regular visual studio (best guess)
+#define SIMDUTF_REGULAR_VISUAL_STUDIO 1
+#endif // __clang__
+#endif // _MSC_VER
+
+#ifdef SIMDUTF_REGULAR_VISUAL_STUDIO
+// https://en.wikipedia.org/wiki/C_alternative_tokens
+// This header should have no effect, except maybe
+// under Visual Studio.
+#include <iso646.h>
+#endif
+
+#if (defined(__x86_64__) || defined(_M_AMD64)) && !defined(_M_ARM64EC)
+#define SIMDUTF_IS_X86_64 1
+#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
+#define SIMDUTF_IS_ARM64 1
+#elif defined(__PPC64__) || defined(_M_PPC64)
+//#define SIMDUTF_IS_PPC64 1
+// The simdutf library does yet support SIMD acceleration under
+// POWER processors. Please see https://github.com/lemire/simdutf/issues/51
+#elif defined(__s390__)
+// s390 IBM system. Big endian.
+#elif (defined(__riscv) || defined(__riscv__)) && __riscv_xlen == 64
+// RISC-V 64-bit
+#define SIMDUTF_IS_RISCV64 1
+
+#if __clang_major__ >= 19
+// Does the compiler support target regions for RISC-V
+#define SIMDUTF_HAS_RVV_TARGET_REGION 1
+#endif
+
+#if __riscv_v_intrinsic >= 11000
+#define SIMDUTF_HAS_RVV_INTRINSICS 1
+#endif
+
+#define SIMDUTF_HAS_ZVBB_INTRINSICS 0 // there is currently no way to detect this
+
+#if SIMDUTF_HAS_RVV_INTRINSICS && __riscv_vector && __riscv_v_min_vlen >= 128 && __riscv_v_elen >= 64
+// RISC-V V extension
+#define SIMDUTF_IS_RVV 1
+#if SIMDUTF_HAS_ZVBB_INTRINSICS && __riscv_zvbb >= 1000000
+// RISC-V Vector Basic Bit-manipulation
+#define SIMDUTF_IS_ZVBB 1
+#endif
+#endif
+
+#elif defined(__loongarch_lp64)
+// LoongArch 64-bit
+#else
+// The simdutf library is designed
+// for 64-bit processors and it seems that you are not
+// compiling for a known 64-bit platform. Please
+// use a 64-bit target such as x64 or 64-bit ARM for best performance.
+#define SIMDUTF_IS_32BITS 1
+
+// We do not support 32-bit platforms, but it can be
+// handy to identify them.
+#if defined(_M_IX86) || defined(__i386__)
+#define SIMDUTF_IS_X86_32BITS 1
+#elif defined(__arm__) || defined(_M_ARM)
+#define SIMDUTF_IS_ARM_32BITS 1
+#elif defined(__PPC__) || defined(_M_PPC)
+#define SIMDUTF_IS_PPC_32BITS 1
+#endif
+
+#endif // defined(__x86_64__) || defined(_M_AMD64)
+
+#ifdef SIMDUTF_IS_32BITS
+#ifndef SIMDUTF_NO_PORTABILITY_WARNING
+// In the future, we may want to warn users of 32-bit systems that
+// the simdutf does not support accelerated kernels for such systems.
+#endif // SIMDUTF_NO_PORTABILITY_WARNING
+#endif // SIMDUTF_IS_32BITS
+
+// this is almost standard?
+#define SIMDUTF_STRINGIFY_IMPLEMENTATION_(a) #a
+#define SIMDUTF_STRINGIFY(a) SIMDUTF_STRINGIFY_IMPLEMENTATION_(a)
+
+// Our fast kernels require 64-bit systems.
+//
+// On 32-bit x86, we lack 64-bit popcnt, lzcnt, blsr instructions.
+// Furthermore, the number of SIMD registers is reduced.
+//
+// On 32-bit ARM, we would have smaller registers.
+//
+// The simdutf users should still have the fallback kernel. It is
+// slower, but it should run everywhere.
+
+//
+// Enable valid runtime implementations, and select SIMDUTF_BUILTIN_IMPLEMENTATION
+//
+
+// We are going to use runtime dispatch.
+#ifdef SIMDUTF_IS_X86_64
+#ifdef __clang__
+// clang does not have GCC push pop
+// warning: clang attribute push can't be used within a namespace in clang up
+// til 8.0 so SIMDUTF_TARGET_REGION and SIMDUTF_UNTARGET_REGION must be *outside* of a
+// namespace.
+#define SIMDUTF_TARGET_REGION(T) \
+ _Pragma(SIMDUTF_STRINGIFY( \
+ clang attribute push(__attribute__((target(T))), apply_to = function)))
+#define SIMDUTF_UNTARGET_REGION _Pragma("clang attribute pop")
+#elif defined(__GNUC__)
+// GCC is easier
+#define SIMDUTF_TARGET_REGION(T) \
+ _Pragma("GCC push_options") _Pragma(SIMDUTF_STRINGIFY(GCC target(T)))
+#define SIMDUTF_UNTARGET_REGION _Pragma("GCC pop_options")
+#endif // clang then gcc
+
+#endif // x86
+
+// Default target region macros don't do anything.
+#ifndef SIMDUTF_TARGET_REGION
+#define SIMDUTF_TARGET_REGION(T)
+#define SIMDUTF_UNTARGET_REGION
+#endif
+
+// Is threading enabled?
+#if defined(_REENTRANT) || defined(_MT)
+#ifndef SIMDUTF_THREADS_ENABLED
+#define SIMDUTF_THREADS_ENABLED
+#endif
+#endif
+
+// workaround for large stack sizes under -O0.
+// https://github.com/simdutf/simdutf/issues/691
+#ifdef __APPLE__
+#ifndef __OPTIMIZE__
+// Apple systems have small stack sizes in secondary threads.
+// Lack of compiler optimization may generate high stack usage.
+// Users may want to disable threads for safety, but only when
+// in debug mode which we detect by the fact that the __OPTIMIZE__
+// macro is not defined.
+#undef SIMDUTF_THREADS_ENABLED
+#endif
+#endif
+
+#ifdef SIMDUTF_VISUAL_STUDIO
+// This is one case where we do not distinguish between
+// regular visual studio and clang under visual studio.
+// clang under Windows has _stricmp (like visual studio) but not strcasecmp (as clang normally has)
+#define simdutf_strcasecmp _stricmp
+#define simdutf_strncasecmp _strnicmp
+#else
+// The strcasecmp, strncasecmp, and strcasestr functions do not work with multibyte strings (e.g. UTF-8).
+// So they are only useful for ASCII in our context.
+// https://www.gnu.org/software/libunistring/manual/libunistring.html#char-_002a-strings
+#define simdutf_strcasecmp strcasecmp
+#define simdutf_strncasecmp strncasecmp
+#endif
+
+#ifdef NDEBUG
+
+#ifdef SIMDUTF_VISUAL_STUDIO
+#define SIMDUTF_UNREACHABLE() __assume(0)
+#define SIMDUTF_ASSUME(COND) __assume(COND)
+#else
+#define SIMDUTF_UNREACHABLE() __builtin_unreachable();
+#define SIMDUTF_ASSUME(COND) do { if (!(COND)) __builtin_unreachable(); } while (0)
+#endif
+
+#else // NDEBUG
+
+#define SIMDUTF_UNREACHABLE() assert(0);
+#define SIMDUTF_ASSUME(COND) assert(COND)
+
+#endif
+
+
+#if defined(__GNUC__) && !defined(__clang__)
+#if __GNUC__ >= 11
+#define SIMDUTF_GCC11ORMORE 1
+#endif // __GNUC__ >= 11
+#endif // defined(__GNUC__) && !defined(__clang__)
+
+
+#endif // SIMDUTF_PORTABILITY_H
+/* end file include/simdutf/portability.h */
+/* begin file include/simdutf/avx512.h */
+#ifndef SIMDUTF_AVX512_H_
+#define SIMDUTF_AVX512_H_
+
+/*
+ It's possible to override AVX512 settings with cmake DCMAKE_CXX_FLAGS.
+
+ All preprocessor directives has form `SIMDUTF_HAS_AVX512{feature}`,
+ where a feature is a code name for extensions.
+
+ Please see the listing below to find which are supported.
+*/
+
+#ifndef SIMDUTF_HAS_AVX512F
+# if defined(__AVX512F__) && __AVX512F__ == 1
+# define SIMDUTF_HAS_AVX512F 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512DQ
+# if defined(__AVX512DQ__) && __AVX512DQ__ == 1
+# define SIMDUTF_HAS_AVX512DQ 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512IFMA
+# if defined(__AVX512IFMA__) && __AVX512IFMA__ == 1
+# define SIMDUTF_HAS_AVX512IFMA 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512CD
+# if defined(__AVX512CD__) && __AVX512CD__ == 1
+# define SIMDUTF_HAS_AVX512CD 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512BW
+# if defined(__AVX512BW__) && __AVX512BW__ == 1
+# define SIMDUTF_HAS_AVX512BW 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512VL
+# if defined(__AVX512VL__) && __AVX512VL__ == 1
+# define SIMDUTF_HAS_AVX512VL 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512VBMI
+# if defined(__AVX512VBMI__) && __AVX512VBMI__ == 1
+# define SIMDUTF_HAS_AVX512VBMI 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512VBMI2
+# if defined(__AVX512VBMI2__) && __AVX512VBMI2__ == 1
+# define SIMDUTF_HAS_AVX512VBMI2 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512VNNI
+# if defined(__AVX512VNNI__) && __AVX512VNNI__ == 1
+# define SIMDUTF_HAS_AVX512VNNI 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512BITALG
+# if defined(__AVX512BITALG__) && __AVX512BITALG__ == 1
+# define SIMDUTF_HAS_AVX512BITALG 1
+# endif
+#endif
+
+#ifndef SIMDUTF_HAS_AVX512VPOPCNTDQ
+# if defined(__AVX512VPOPCNTDQ__) && __AVX512VPOPCNTDQ__ == 1
+# define SIMDUTF_HAS_AVX512VPOPCNTDQ 1
+# endif
+#endif
+
+#endif // SIMDUTF_AVX512_H_
+/* end file include/simdutf/avx512.h */
+
+
+#if defined(__GNUC__)
+ // Marks a block with a name so that MCA analysis can see it.
+ #define SIMDUTF_BEGIN_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-BEGIN " #name);
+ #define SIMDUTF_END_DEBUG_BLOCK(name) __asm volatile("# LLVM-MCA-END " #name);
+ #define SIMDUTF_DEBUG_BLOCK(name, block) BEGIN_DEBUG_BLOCK(name); block; END_DEBUG_BLOCK(name);
+#else
+ #define SIMDUTF_BEGIN_DEBUG_BLOCK(name)
+ #define SIMDUTF_END_DEBUG_BLOCK(name)
+ #define SIMDUTF_DEBUG_BLOCK(name, block)
+#endif
+
+// Align to N-byte boundary
+#define SIMDUTF_ROUNDUP_N(a, n) (((a) + ((n)-1)) & ~((n)-1))
+#define SIMDUTF_ROUNDDOWN_N(a, n) ((a) & ~((n)-1))
+
+#define SIMDUTF_ISALIGNED_N(ptr, n) (((uintptr_t)(ptr) & ((n)-1)) == 0)
+
+#if defined(SIMDUTF_REGULAR_VISUAL_STUDIO)
+
+ #define simdutf_really_inline __forceinline
+ #define simdutf_never_inline __declspec(noinline)
+
+ #define simdutf_unused
+ #define simdutf_warn_unused
+
+ #ifndef simdutf_likely
+ #define simdutf_likely(x) x
+ #endif
+ #ifndef simdutf_unlikely
+ #define simdutf_unlikely(x) x
+ #endif
+
+ #define SIMDUTF_PUSH_DISABLE_WARNINGS __pragma(warning( push ))
+ #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS __pragma(warning( push, 0 ))
+ #define SIMDUTF_DISABLE_VS_WARNING(WARNING_NUMBER) __pragma(warning( disable : WARNING_NUMBER ))
+ // Get rid of Intellisense-only warnings (Code Analysis)
+ // Though __has_include is C++17, it is supported in Visual Studio 2017 or better (_MSC_VER>=1910).
+ #ifdef __has_include
+ #if __has_include(<CppCoreCheck\Warnings.h>)
+ #include <CppCoreCheck\Warnings.h>
+ #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS SIMDUTF_DISABLE_VS_WARNING(ALL_CPPCORECHECK_WARNINGS)
+ #endif
+ #endif
+
+ #ifndef SIMDUTF_DISABLE_UNDESIRED_WARNINGS
+ #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS
+ #endif
+
+ #define SIMDUTF_DISABLE_DEPRECATED_WARNING SIMDUTF_DISABLE_VS_WARNING(4996)
+ #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING
+ #define SIMDUTF_POP_DISABLE_WARNINGS __pragma(warning( pop ))
+
+#else // SIMDUTF_REGULAR_VISUAL_STUDIO
+
+ #define simdutf_really_inline inline __attribute__((always_inline))
+ #define simdutf_never_inline inline __attribute__((noinline))
+
+ #define simdutf_unused __attribute__((unused))
+ #define simdutf_warn_unused __attribute__((warn_unused_result))
+
+ #ifndef simdutf_likely
+ #define simdutf_likely(x) __builtin_expect(!!(x), 1)
+ #endif
+ #ifndef simdutf_unlikely
+ #define simdutf_unlikely(x) __builtin_expect(!!(x), 0)
+ #endif
+
+ #define SIMDUTF_PUSH_DISABLE_WARNINGS _Pragma("GCC diagnostic push")
+ // gcc doesn't seem to disable all warnings with all and extra, add warnings here as necessary
+ #define SIMDUTF_PUSH_DISABLE_ALL_WARNINGS SIMDUTF_PUSH_DISABLE_WARNINGS \
+ SIMDUTF_DISABLE_GCC_WARNING(-Weffc++) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wall) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wconversion) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wextra) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wattributes) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wimplicit-fallthrough) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wnon-virtual-dtor) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wreturn-type) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wshadow) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wunused-parameter) \
+ SIMDUTF_DISABLE_GCC_WARNING(-Wunused-variable)
+ #define SIMDUTF_PRAGMA(P) _Pragma(#P)
+ #define SIMDUTF_DISABLE_GCC_WARNING(WARNING) SIMDUTF_PRAGMA(GCC diagnostic ignored #WARNING)
+ #if defined(SIMDUTF_CLANG_VISUAL_STUDIO)
+ #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS SIMDUTF_DISABLE_GCC_WARNING(-Wmicrosoft-include)
+ #else
+ #define SIMDUTF_DISABLE_UNDESIRED_WARNINGS
+ #endif
+ #define SIMDUTF_DISABLE_DEPRECATED_WARNING SIMDUTF_DISABLE_GCC_WARNING(-Wdeprecated-declarations)
+ #define SIMDUTF_DISABLE_STRICT_OVERFLOW_WARNING SIMDUTF_DISABLE_GCC_WARNING(-Wstrict-overflow)
+ #define SIMDUTF_POP_DISABLE_WARNINGS _Pragma("GCC diagnostic pop")
+
+
+
+#endif // MSC_VER
+
+#ifndef SIMDUTF_DLLIMPORTEXPORT
+ #if defined(SIMDUTF_VISUAL_STUDIO)
+ /**
+ * It does not matter here whether you are using
+ * the regular visual studio or clang under visual
+ * studio.
+ */
+ #if SIMDUTF_USING_LIBRARY
+ #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllimport)
+ #else
+ #define SIMDUTF_DLLIMPORTEXPORT __declspec(dllexport)
+ #endif
+ #else
+ #define SIMDUTF_DLLIMPORTEXPORT
+ #endif
+#endif
+
+/// If EXPR is an error, returns it.
+#define SIMDUTF_TRY(EXPR) { auto _err = (EXPR); if (_err) { return _err; } }
+
+
+#endif // SIMDUTF_COMMON_DEFS_H
+/* end file include/simdutf/common_defs.h */
+/* begin file include/simdutf/encoding_types.h */
+#include <string>
+
+namespace simdutf {
+
+enum encoding_type {
+ UTF8 = 1, // BOM 0xef 0xbb 0xbf
+ UTF16_LE = 2, // BOM 0xff 0xfe
+ UTF16_BE = 4, // BOM 0xfe 0xff
+ UTF32_LE = 8, // BOM 0xff 0xfe 0x00 0x00
+ UTF32_BE = 16, // BOM 0x00 0x00 0xfe 0xff
+ Latin1 = 32,
+
+ unspecified = 0
+};
+
+enum endianness {
+ LITTLE = 0,
+ BIG = 1
+};
+
+bool match_system(endianness e);
+
+std::string to_string(encoding_type bom);
+
+// Note that BOM for UTF8 is discouraged.
+namespace BOM {
+
+/**
+ * Checks for a BOM. If not, returns unspecified
+ * @param input the string to process
+ * @param length the length of the string in code units
+ * @return the corresponding encoding
+ */
+
+encoding_type check_bom(const uint8_t* byte, size_t length);
+encoding_type check_bom(const char* byte, size_t length);
+/**
+ * Returns the size, in bytes, of the BOM for a given encoding type.
+ * Note that UTF8 BOM are discouraged.
+ * @param bom the encoding type
+ * @return the size in bytes of the corresponding BOM
+ */
+size_t bom_byte_size(encoding_type bom);
+
+} // BOM namespace
+} // simdutf namespace
+/* end file include/simdutf/encoding_types.h */
+/* begin file include/simdutf/error.h */
+#ifndef SIMDUTF_ERROR_H
+#define SIMDUTF_ERROR_H
+namespace simdutf {
+
+enum error_code {
+ SUCCESS = 0,
+ HEADER_BITS, // Any byte must have fewer than 5 header bits.
+ TOO_SHORT, // The leading byte must be followed by N-1 continuation bytes, where N is the UTF-8 character length
+ // This is also the error when the input is truncated.
+ TOO_LONG, // We either have too many consecutive continuation bytes or the string starts with a continuation byte.
+ OVERLONG, // The decoded character must be above U+7F for two-byte characters, U+7FF for three-byte characters,
+ // and U+FFFF for four-byte characters.
+ TOO_LARGE, // The decoded character must be less than or equal to U+10FFFF,less than or equal than U+7F for ASCII OR less than equal than U+FF for Latin1
+ SURROGATE, // The decoded character must be not be in U+D800...DFFF (UTF-8 or UTF-32) OR
+ // a high surrogate must be followed by a low surrogate and a low surrogate must be preceded by a high surrogate (UTF-16) OR
+ // there must be no surrogate at all (Latin1)
+ INVALID_BASE64_CHARACTER, // Found a character that cannot be part of a valid base64 string.
+ BASE64_INPUT_REMAINDER, // The base64 input terminates with a single character, excluding padding (=).
+ OUTPUT_BUFFER_TOO_SMALL, // The provided buffer is too small.
+ OTHER // Not related to validation/transcoding.
+};
+
+struct result {
+ error_code error;
+ size_t count; // In case of error, indicates the position of the error. In case of success, indicates the number of code units validated/written.
+
+ simdutf_really_inline result() : error{error_code::SUCCESS}, count{0} {}
+
+ simdutf_really_inline result(error_code _err, size_t _pos) : error{_err}, count{_pos} {}
+};
+
+}
+#endif
+/* end file include/simdutf/error.h */
+
+SIMDUTF_PUSH_DISABLE_WARNINGS
+SIMDUTF_DISABLE_UNDESIRED_WARNINGS
+
+// Public API
+/* begin file include/simdutf/simdutf_version.h */
+// /include/simdutf/simdutf_version.h automatically generated by release.py,
+// do not change by hand
+#ifndef SIMDUTF_SIMDUTF_VERSION_H
+#define SIMDUTF_SIMDUTF_VERSION_H
+
+/** The version of simdutf being used (major.minor.revision) */
+#define SIMDUTF_VERSION "5.2.8"
+
+namespace simdutf {
+enum {
+ /**
+ * The major version (MAJOR.minor.revision) of simdutf being used.
+ */
+ SIMDUTF_VERSION_MAJOR = 5,
+ /**
+ * The minor version (major.MINOR.revision) of simdutf being used.
+ */
+ SIMDUTF_VERSION_MINOR = 2,
+ /**
+ * The revision (major.minor.REVISION) of simdutf being used.
+ */
+ SIMDUTF_VERSION_REVISION = 8
+};
+} // namespace simdutf
+
+#endif // SIMDUTF_SIMDUTF_VERSION_H
+/* end file include/simdutf/simdutf_version.h */
+/* begin file include/simdutf/implementation.h */
+#ifndef SIMDUTF_IMPLEMENTATION_H
+#define SIMDUTF_IMPLEMENTATION_H
+#include <string>
+#if !defined(SIMDUTF_NO_THREADS)
+#include <atomic>
+#endif
+#include <vector>
+#include <tuple>
+/* begin file include/simdutf/internal/isadetection.h */
+/* From
+https://github.com/endorno/pytorch/blob/master/torch/lib/TH/generic/simd/simd.h
+Highly modified.
+
+Copyright (c) 2016- Facebook, Inc (Adam Paszke)
+Copyright (c) 2014- Facebook, Inc (Soumith Chintala)
+Copyright (c) 2011-2014 Idiap Research Institute (Ronan Collobert)
+Copyright (c) 2012-2014 Deepmind Technologies (Koray Kavukcuoglu)
+Copyright (c) 2011-2012 NEC Laboratories America (Koray Kavukcuoglu)
+Copyright (c) 2011-2013 NYU (Clement Farabet)
+Copyright (c) 2006-2010 NEC Laboratories America (Ronan Collobert, Leon Bottou,
+Iain Melvin, Jason Weston) Copyright (c) 2006 Idiap Research Institute
+(Samy Bengio) Copyright (c) 2001-2004 Idiap Research Institute (Ronan Collobert,
+Samy Bengio, Johnny Mariethoz)
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+3. Neither the names of Facebook, Deepmind Technologies, NYU, NEC Laboratories
+America and IDIAP Research Institute nor the names of its contributors may be
+ used to endorse or promote products derived from this software without
+ specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+*/
+
+#ifndef SIMDutf_INTERNAL_ISADETECTION_H
+#define SIMDutf_INTERNAL_ISADETECTION_H
+
+#include <cstdint>
+#include <cstdlib>
+#if defined(_MSC_VER)
+#include <intrin.h>
+#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
+#include <cpuid.h>
+#endif
+
+
+// RISC-V ISA detection utilities
+#if SIMDUTF_IS_RISCV64 && defined(__linux__)
+#include <unistd.h> // for syscall
+// We define these ourselves, for backwards compatibility
+struct simdutf_riscv_hwprobe { int64_t key; uint64_t value; };
+#define simdutf_riscv_hwprobe(...) syscall(258, __VA_ARGS__)
+#define SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0 4
+#define SIMDUTF_RISCV_HWPROBE_IMA_V (1 << 2)
+#define SIMDUTF_RISCV_HWPROBE_EXT_ZVBB (1 << 17)
+#endif // SIMDUTF_IS_RISCV64 && defined(__linux__)
+
+namespace simdutf {
+namespace internal {
+
+enum instruction_set {
+ DEFAULT = 0x0,
+ NEON = 0x1,
+ AVX2 = 0x4,
+ SSE42 = 0x8,
+ PCLMULQDQ = 0x10,
+ BMI1 = 0x20,
+ BMI2 = 0x40,
+ ALTIVEC = 0x80,
+ AVX512F = 0x100,
+ AVX512DQ = 0x200,
+ AVX512IFMA = 0x400,
+ AVX512PF = 0x800,
+ AVX512ER = 0x1000,
+ AVX512CD = 0x2000,
+ AVX512BW = 0x4000,
+ AVX512VL = 0x8000,
+ AVX512VBMI2 = 0x10000,
+ AVX512VPOPCNTDQ = 0x2000,
+ RVV = 0x4000,
+ ZVBB = 0x8000,
+};
+
+#if defined(__PPC64__)
+
+static inline uint32_t detect_supported_architectures() {
+ return instruction_set::ALTIVEC;
+}
+
+#elif SIMDUTF_IS_RISCV64
+
+static inline uint32_t detect_supported_architectures() {
+ uint32_t host_isa = instruction_set::DEFAULT;
+#if SIMDUTF_IS_RVV
+ host_isa |= instruction_set::RVV;
+#endif
+#if SIMDUTF_IS_ZVBB
+ host_isa |= instruction_set::ZVBB;
+#endif
+#if defined(__linux__)
+ simdutf_riscv_hwprobe probes[] = { { SIMDUTF_RISCV_HWPROBE_KEY_IMA_EXT_0, 0 } };
+ long ret = simdutf_riscv_hwprobe(&probes, sizeof probes/sizeof *probes, 0, nullptr, 0);
+ if (ret == 0) {
+ uint64_t extensions = probes[0].value;
+ if (extensions & SIMDUTF_RISCV_HWPROBE_IMA_V)
+ host_isa |= instruction_set::RVV;
+ if (extensions & SIMDUTF_RISCV_HWPROBE_EXT_ZVBB)
+ host_isa |= instruction_set::ZVBB;
+ }
+#endif
+#if defined(RUN_IN_SPIKE_SIMULATOR)
+ // Proxy Kernel does not implement yet hwprobe syscall
+ host_isa |= instruction_set::RVV;
+#endif
+ return host_isa;
+}
+
+#elif defined(__aarch64__) || defined(_M_ARM64) || defined(_M_ARM64EC)
+
+static inline uint32_t detect_supported_architectures() {
+ return instruction_set::NEON;
+}
+
+#elif defined(__x86_64__) || defined(_M_AMD64) // x64
+
+
+namespace {
+namespace cpuid_bit {
+ // Can be found on Intel ISA Reference for CPUID
+
+ // EAX = 0x01
+ constexpr uint32_t pclmulqdq = uint32_t(1) << 1; ///< @private bit 1 of ECX for EAX=0x1
+ constexpr uint32_t sse42 = uint32_t(1) << 20; ///< @private bit 20 of ECX for EAX=0x1
+ constexpr uint32_t osxsave = (uint32_t(1) << 26) | (uint32_t(1) << 27); ///< @private bits 26+27 of ECX for EAX=0x1
+
+ // EAX = 0x7f (Structured Extended Feature Flags), ECX = 0x00 (Sub-leaf)
+ // See: "Table 3-8. Information Returned by CPUID Instruction"
+ namespace ebx {
+ constexpr uint32_t bmi1 = uint32_t(1) << 3;
+ constexpr uint32_t avx2 = uint32_t(1) << 5;
+ constexpr uint32_t bmi2 = uint32_t(1) << 8;
+ constexpr uint32_t avx512f = uint32_t(1) << 16;
+ constexpr uint32_t avx512dq = uint32_t(1) << 17;
+ constexpr uint32_t avx512ifma = uint32_t(1) << 21;
+ constexpr uint32_t avx512cd = uint32_t(1) << 28;
+ constexpr uint32_t avx512bw = uint32_t(1) << 30;
+ constexpr uint32_t avx512vl = uint32_t(1) << 31;
+ }
+
+ namespace ecx {
+ constexpr uint32_t avx512vbmi = uint32_t(1) << 1;
+ constexpr uint32_t avx512vbmi2 = uint32_t(1) << 6;
+ constexpr uint32_t avx512vnni = uint32_t(1) << 11;
+ constexpr uint32_t avx512bitalg = uint32_t(1) << 12;
+ constexpr uint32_t avx512vpopcnt = uint32_t(1) << 14;
+ }
+ namespace edx {
+ constexpr uint32_t avx512vp2intersect = uint32_t(1) << 8;
+ }
+ namespace xcr0_bit {
+ constexpr uint64_t avx256_saved = uint64_t(1) << 2; ///< @private bit 2 = AVX
+ constexpr uint64_t avx512_saved = uint64_t(7) << 5; ///< @private bits 5,6,7 = opmask, ZMM_hi256, hi16_ZMM
+ }
+ }
+}
+
+
+
+static inline void cpuid(uint32_t *eax, uint32_t *ebx, uint32_t *ecx,
+ uint32_t *edx) {
+#if defined(_MSC_VER)
+ int cpu_info[4];
+ __cpuidex(cpu_info, *eax, *ecx);
+ *eax = cpu_info[0];
+ *ebx = cpu_info[1];
+ *ecx = cpu_info[2];
+ *edx = cpu_info[3];
+#elif defined(HAVE_GCC_GET_CPUID) && defined(USE_GCC_GET_CPUID)
+ uint32_t level = *eax;
+ __get_cpuid(level, eax, ebx, ecx, edx);
+#else
+ uint32_t a = *eax, b, c = *ecx, d;
+ asm volatile("cpuid\n\t" : "+a"(a), "=b"(b), "+c"(c), "=d"(d));
+ *eax = a;
+ *ebx = b;
+ *ecx = c;
+ *edx = d;
+#endif
+}
+
+static inline uint64_t xgetbv() {
+ #if defined(_MSC_VER)
+ return _xgetbv(0);
+ #else
+ uint32_t xcr0_lo, xcr0_hi;
+ asm volatile("xgetbv\n\t" : "=a" (xcr0_lo), "=d" (xcr0_hi) : "c" (0));
+ return xcr0_lo | ((uint64_t)xcr0_hi << 32);
+ #endif
+ }
+
+static inline uint32_t detect_supported_architectures() {
+ uint32_t eax;
+ uint32_t ebx = 0;
+ uint32_t ecx = 0;
+ uint32_t edx = 0;
+ uint32_t host_isa = 0x0;
+
+ // EBX for EAX=0x1
+ eax = 0x1;
+ cpuid(&eax, &ebx, &ecx, &edx);
+
+ if (ecx & cpuid_bit::sse42) {
+ host_isa |= instruction_set::SSE42;
+ }
+
+ if (ecx & cpuid_bit::pclmulqdq) {
+ host_isa |= instruction_set::PCLMULQDQ;
+ }
+
+ if ((ecx & cpuid_bit::osxsave) != cpuid_bit::osxsave) {
+ return host_isa;
+ }
+
+ // xgetbv for checking if the OS saves registers
+ uint64_t xcr0 = xgetbv();
+
+ if ((xcr0 & cpuid_bit::xcr0_bit::avx256_saved) == 0) {
+ return host_isa;
+ }
+ // ECX for EAX=0x7
+ eax = 0x7;
+ ecx = 0x0; // Sub-leaf = 0
+ cpuid(&eax, &ebx, &ecx, &edx);
+ if (ebx & cpuid_bit::ebx::avx2) {
+ host_isa |= instruction_set::AVX2;
+ }
+ if (ebx & cpuid_bit::ebx::bmi1) {
+ host_isa |= instruction_set::BMI1;
+ }
+ if (ebx & cpuid_bit::ebx::bmi2) {
+ host_isa |= instruction_set::BMI2;
+ }
+ if (!((xcr0 & cpuid_bit::xcr0_bit::avx512_saved) == cpuid_bit::xcr0_bit::avx512_saved)) {
+ return host_isa;
+ }
+ if (ebx & cpuid_bit::ebx::avx512f) {
+ host_isa |= instruction_set::AVX512F;
+ }
+ if (ebx & cpuid_bit::ebx::avx512bw) {
+ host_isa |= instruction_set::AVX512BW;
+ }
+ if (ebx & cpuid_bit::ebx::avx512cd) {
+ host_isa |= instruction_set::AVX512CD;
+ }
+ if (ebx & cpuid_bit::ebx::avx512dq) {
+ host_isa |= instruction_set::AVX512DQ;
+ }
+ if (ebx & cpuid_bit::ebx::avx512vl) {
+ host_isa |= instruction_set::AVX512VL;
+ }
+ if (ecx & cpuid_bit::ecx::avx512vbmi2) {
+ host_isa |= instruction_set::AVX512VBMI2;
+ }
+ if (ecx & cpuid_bit::ecx::avx512vpopcnt) {
+ host_isa |= instruction_set::AVX512VPOPCNTDQ;
+ }
+ return host_isa;
+}
+#else // fallback
+
+// includes 32-bit ARM.
+static inline uint32_t detect_supported_architectures() {
+ return instruction_set::DEFAULT;
+}
+
+
+#endif // end SIMD extension detection code
+
+} // namespace internal
+} // namespace simdutf
+
+#endif // SIMDutf_INTERNAL_ISADETECTION_H
+/* end file include/simdutf/internal/isadetection.h */
+
+
+namespace simdutf {
+
+/**
+ * Autodetect the encoding of the input, a single encoding is recommended.
+ * E.g., the function might return simdutf::encoding_type::UTF8,
+ * simdutf::encoding_type::UTF16_LE, simdutf::encoding_type::UTF16_BE, or
+ * simdutf::encoding_type::UTF32_LE.
+ *
+ * @param input the string to analyze.
+ * @param length the length of the string in bytes.
+ * @return the detected encoding type
+ */
+simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const char * input, size_t length) noexcept;
+simdutf_really_inline simdutf_warn_unused simdutf::encoding_type autodetect_encoding(const uint8_t * input, size_t length) noexcept {
+ return autodetect_encoding(reinterpret_cast<const char *>(input), length);
+}
+
+/**
+ * Autodetect the possible encodings of the input in one pass.
+ * E.g., if the input might be UTF-16LE or UTF-8, this function returns
+ * the value (simdutf::encoding_type::UTF8 | simdutf::encoding_type::UTF16_LE).
+ *
+ * Overridden by each implementation.
+ *
+ * @param input the string to analyze.
+ * @param length the length of the string in bytes.
+ * @return the detected encoding type
+ */
+simdutf_warn_unused int detect_encodings(const char * input, size_t length) noexcept;
+simdutf_really_inline simdutf_warn_unused int detect_encodings(const uint8_t * input, size_t length) noexcept {
+ return detect_encodings(reinterpret_cast<const char *>(input), length);
+}
+
+/**
+ * Validate the UTF-8 string. This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf8_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the UTF-8 string to validate.
+ * @param len the length of the string in bytes.
+ * @return true if and only if the string is valid UTF-8.
+ */
+simdutf_warn_unused bool validate_utf8(const char *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-8 string and stop on error.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the UTF-8 string to validate.
+ * @param len the length of the string in bytes.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_utf8_with_errors(const char *buf, size_t len) noexcept;
+
+/**
+ * Validate the ASCII string.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the ASCII string to validate.
+ * @param len the length of the string in bytes.
+ * @return true if and only if the string is valid ASCII.
+ */
+simdutf_warn_unused bool validate_ascii(const char *buf, size_t len) noexcept;
+
+/**
+ * Validate the ASCII string and stop on error. It might be faster than
+ * validate_utf8 when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the ASCII string to validate.
+ * @param len the length of the string in bytes.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_ascii_with_errors(const char *buf, size_t len) noexcept;
+
+/**
+ * Using native endianness; Validate the UTF-16 string.
+ * This function may be best when you expect the input to be almost always valid.
+ * Otherwise, consider using validate_utf16_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16 string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return true if and only if the string is valid UTF-16.
+ */
+simdutf_warn_unused bool validate_utf16(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-16LE string. This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf16le_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16LE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return true if and only if the string is valid UTF-16LE.
+ */
+simdutf_warn_unused bool validate_utf16le(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-16BE string. This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf16be_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16BE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return true if and only if the string is valid UTF-16BE.
+ */
+simdutf_warn_unused bool validate_utf16be(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Using native endianness; Validate the UTF-16 string and stop on error.
+ * It might be faster than validate_utf16 when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16 string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_utf16_with_errors(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-16LE string and stop on error. It might be faster than
+ * validate_utf16le when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16LE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_utf16le_with_errors(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-16BE string and stop on error. It might be faster than
+ * validate_utf16be when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16BE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_utf16be_with_errors(const char16_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-32 string. This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf32_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-32 string to validate.
+ * @param len the length of the string in number of 4-byte code units (char32_t).
+ * @return true if and only if the string is valid UTF-32.
+ */
+simdutf_warn_unused bool validate_utf32(const char32_t *buf, size_t len) noexcept;
+
+/**
+ * Validate the UTF-32 string and stop on error. It might be faster than
+ * validate_utf32 when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-32 string to validate.
+ * @param len the length of the string in number of 4-byte code units (char32_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+simdutf_warn_unused result validate_utf32_with_errors(const char32_t *buf, size_t len) noexcept;
+
+ /**
+ * Convert Latin1 string into UTF8 string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if conversion is not possible
+ */
+ simdutf_warn_unused size_t convert_latin1_to_utf8(const char * input, size_t length, char* utf8_output) noexcept;
+
+
+ /**
+ * Convert possibly Latin1 string into UTF-16LE string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused size_t convert_latin1_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+ /**
+ * Convert Latin1 string into UTF-16BE string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused size_t convert_latin1_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+ /**
+ * Convert Latin1 string into UTF-32 string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char32_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused size_t convert_latin1_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+ /**
+ * Convert possibly broken UTF-8 string into latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if the input was not valid UTF-8 string or if it cannot be represented as Latin1
+ */
+ simdutf_warn_unused size_t convert_utf8_to_latin1(const char * input, size_t length, char* latin1_output) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-8 string into a UTF-16 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+simdutf_warn_unused size_t convert_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+
+/**
+ * Using native endianness, convert a Latin1 string into a UTF-16 string.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t.
+ */
+simdutf_warn_unused size_t convert_latin1_to_utf16(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-16LE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+simdutf_warn_unused size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-16BE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+simdutf_warn_unused size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+
+ /**
+ * Convert possibly broken UTF-8 string into latin1 string with errors.
+ * If the string cannot be represented as Latin1, an error
+ * code is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused result convert_utf8_to_latin1_with_errors(const char * input, size_t length, char* latin1_output) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-8 string into UTF-16
+ * string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf8_to_utf16_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char32_t; 0 if the input was not valid UTF-8 string
+ */
+simdutf_warn_unused size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) noexcept;
+
+/**
+ * Convert possibly broken UTF-8 string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+simdutf_warn_unused result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) noexcept;
+
+ /**
+ * Convert valid UTF-8 string into latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if the input was not valid UTF-8 string
+ */
+ simdutf_warn_unused size_t convert_valid_utf8_to_latin1(const char * input, size_t length, char* latin1_output) noexcept;
+
+
+/**
+ * Using native endianness, convert valid UTF-8 string into a UTF-16 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t
+ */
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16(const char * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert valid UTF-8 string into UTF-16LE string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t
+ */
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert valid UTF-8 string into UTF-16BE string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t
+ */
+simdutf_warn_unused size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert valid UTF-8 string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char32_t
+ */
+simdutf_warn_unused size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+
+/**
+ * Return the number of bytes that this Latin1 string would require in UTF-8 format.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string bytes
+ * @return the number of bytes required to encode the Latin1 string as UTF-8
+ */
+simdutf_warn_unused size_t utf8_length_from_latin1(const char * input, size_t length) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-8 string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in byte
+ * @return the number of bytes required to encode the UTF-8 string as Latin1
+ */
+simdutf_warn_unused size_t latin1_length_from_utf8(const char * input, size_t length) noexcept;
+
+/**
+ * Compute the number of 2-byte code units that this UTF-8 string would require in UTF-16LE format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return the number of char16_t code units required to encode the UTF-8 string as UTF-16LE
+ */
+simdutf_warn_unused size_t utf16_length_from_utf8(const char * input, size_t length) noexcept;
+
+/**
+ * Compute the number of 4-byte code units that this UTF-8 string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf8
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return the number of char32_t code units required to encode the UTF-8 string as UTF-32
+ */
+simdutf_warn_unused size_t utf32_length_from_utf8(const char * input, size_t length) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16 string or if it cannot be represented as Latin1
+ */
+simdutf_warn_unused size_t convert_utf16_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16LE string into Latin1 string.
+ * If the string cannot be represented as Latin1, an error
+ * is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string or if it cannot be represented as Latin1
+ */
+simdutf_warn_unused size_t convert_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16BE string or if it cannot be represented as Latin1
+ */
+simdutf_warn_unused size_t convert_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+
+/**
+ * Convert possibly broken UTF-16LE string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16LE string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16le_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into Latin1 string.
+ * If the string cannot be represented as Latin1, an error
+ * is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16be_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16le_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf16be_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Using native endianness, convert valid UTF-16 string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+
+/**
+ * Using native endianness, convert UTF-16 string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert valid UTF-16LE string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert valid UTF-16BE string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) noexcept;
+
+
+/**
+ * Convert valid UTF-16LE string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert valid UTF-16BE string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16LE string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+simdutf_warn_unused size_t convert_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-16 string into
+ * UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+simdutf_warn_unused result convert_utf16_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+simdutf_warn_unused result convert_utf16le_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+simdutf_warn_unused result convert_utf16be_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Using native endianness, convert valid UTF-16 string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-16 (native endianness).
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert valid UTF-16LE string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+/**
+ * Convert valid UTF-16BE string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) noexcept;
+
+
+/*
+ * Compute the number of bytes that this UTF-16LE/BE string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as Latin1
+ */
+simdutf_warn_unused size_t latin1_length_from_utf16(size_t length) noexcept;
+
+
+/**
+ * Using native endianness; Compute the number of bytes that this UTF-16
+ * string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-8
+ */
+simdutf_warn_unused size_t utf8_length_from_utf16(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-16LE string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-8
+ */
+simdutf_warn_unused size_t utf8_length_from_utf16le(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-16BE string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16BE string as UTF-8
+ */
+simdutf_warn_unused size_t utf8_length_from_utf16be(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+simdutf_warn_unused size_t convert_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf32_to_utf8_with_errors(const char32_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Convert valid UTF-32 string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-32 string into a UTF-16 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+simdutf_warn_unused size_t convert_utf32_to_utf16(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-16LE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+simdutf_warn_unused size_t convert_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string or if it cannot be represented as Latin1
+ */
+simdutf_warn_unused size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) noexcept;
+
+
+/**
+ * Convert possibly broken UTF-32 string into Latin1 string and stop on error.
+ * If the string cannot be represented as Latin1, an error is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+simdutf_warn_unused result convert_utf32_to_latin1_with_errors(const char32_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert valid UTF-32 string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-16BE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+simdutf_warn_unused size_t convert_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Using native endianness, convert possibly broken UTF-32 string into UTF-16
+ * string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf32_to_utf16_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf32_to_utf16le_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+simdutf_warn_unused result convert_utf32_to_utf16be_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Using native endianness, convert valid UTF-32 string into a UTF-16 string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert valid UTF-32 string into UTF-16LE string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Convert valid UTF-32 string into UTF-16BE string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+simdutf_warn_unused size_t convert_valid_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) noexcept;
+
+/**
+ * Change the endianness of the input. Can be used to go from UTF-16LE to UTF-16BE or
+ * from UTF-16BE to UTF-16LE.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param output the pointer to buffer that can hold the conversion result
+ */
+void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-32 string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as UTF-8
+ */
+simdutf_warn_unused size_t utf8_length_from_utf32(const char32_t * input, size_t length) noexcept;
+
+/**
+ * Compute the number of two-byte code units that this UTF-32 string would require in UTF-16 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as UTF-16
+ */
+simdutf_warn_unused size_t utf16_length_from_utf32(const char32_t * input, size_t length) noexcept;
+
+/**
+ * Using native endianness; Compute the number of bytes that this UTF-16
+ * string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf16.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-32
+ */
+simdutf_warn_unused size_t utf32_length_from_utf16(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-16LE string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf16le.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-32
+ */
+simdutf_warn_unused size_t utf32_length_from_utf16le(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Compute the number of bytes that this UTF-16BE string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf16be.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16BE string as UTF-32
+ */
+simdutf_warn_unused size_t utf32_length_from_utf16be(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-16 (native endianness).
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return number of code points
+ */
+simdutf_warn_unused size_t count_utf16(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return number of code points
+ */
+simdutf_warn_unused size_t count_utf16le(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return number of code points
+ */
+simdutf_warn_unused size_t count_utf16be(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return number of code points
+ */
+simdutf_warn_unused size_t count_utf8(const char * input, size_t length) noexcept;
+
+/**
+ * Given a valid UTF-8 string having a possibly truncated last character,
+ * this function checks the end of string. If the last character is truncated (or partial),
+ * then it returns a shorter length (shorter by 1 to 3 bytes) so that the short UTF-8
+ * strings only contain complete characters. If there is no truncated character,
+ * the original length is returned.
+ *
+ * This function assumes that the input string is valid UTF-8, but possibly truncated.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return the length of the string in bytes, possibly shorter by 1 to 3 bytes
+ */
+simdutf_warn_unused size_t trim_partial_utf8(const char *input, size_t length);
+
+/**
+ * Given a valid UTF-16BE string having a possibly truncated last character,
+ * this function checks the end of string. If the last character is truncated (or partial),
+ * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16BE
+ * strings only contain complete characters. If there is no truncated character,
+ * the original length is returned.
+ *
+ * This function assumes that the input string is valid UTF-16BE, but possibly truncated.
+ *
+ * @param input the UTF-16BE string to process
+ * @param length the length of the string in bytes
+ * @return the length of the string in bytes, possibly shorter by 1 unit
+ */
+simdutf_warn_unused size_t trim_partial_utf16be(const char16_t* input, size_t length);
+
+/**
+ * Given a valid UTF-16LE string having a possibly truncated last character,
+ * this function checks the end of string. If the last character is truncated (or partial),
+ * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16LE
+ * strings only contain complete characters. If there is no truncated character,
+ * the original length is returned.
+ *
+ * This function assumes that the input string is valid UTF-16LE, but possibly truncated.
+ *
+ * @param input the UTF-16LE string to process
+ * @param length the length of the string in bytes
+ * @return the length of the string in unit, possibly shorter by 1 unit
+ */
+simdutf_warn_unused size_t trim_partial_utf16le(const char16_t* input, size_t length);
+
+
+/**
+ * Given a valid UTF-16 string having a possibly truncated last character,
+ * this function checks the end of string. If the last character is truncated (or partial),
+ * then it returns a shorter length (shorter by 1 unit) so that the short UTF-16
+ * strings only contain complete characters. If there is no truncated character,
+ * the original length is returned.
+ *
+ * This function assumes that the input string is valid UTF-16, but possibly truncated.
+ * We use the native endianness.
+ *
+ * @param input the UTF-16 string to process
+ * @param length the length of the string in bytes
+ * @return the length of the string in unit, possibly shorter by 1 unit
+ */
+simdutf_warn_unused size_t trim_partial_utf16(const char16_t* input, size_t length);
+
+// base64_options are used to specify the base64 encoding options.
+using base64_options = uint64_t;
+enum : base64_options {
+ base64_default = 0, /* standard base64 format */
+ base64_url = 1 /* base64url format*/
+};
+
+/**
+ * Provide the maximal binary length in bytes given the base64 input.
+ * In general, if the input contains ASCII spaces, the result will be less than
+ * the maximum length.
+ *
+ * @param input the base64 input to process
+ * @param length the length of the base64 input in bytes
+ * @return maximum number of binary bytes
+ */
+simdutf_warn_unused size_t maximal_binary_length_from_base64(const char * input, size_t length) noexcept;
+
+/**
+ * Provide the maximal binary length in bytes given the base64 input.
+ * In general, if the input contains ASCII spaces, the result will be less than
+ * the maximum length.
+ *
+ * @param input the base64 input to process, in ASCII stored as 16-bit units
+ * @param length the length of the base64 input in 16-bit units
+ * @return maximal number of binary bytes
+ */
+simdutf_warn_unused size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) noexcept;
+
+/**
+ * Convert a base64 input to a binary ouput.
+ *
+ * This function follows the WHATWG forgiving-base64 format, which means that it will
+ * ignore any ASCII spaces in the input. You may provide a padded input (with one or two
+ * equal signs at the end) or an unpadded input (without any equal signs at the end).
+ *
+ * See https://infra.spec.whatwg.org/#forgiving-base64-decode
+ *
+ * This function will fail in case of invalid input. There are two possible reasons for
+ * failure: the input contains a number of base64 characters that when divided by 4, leaves
+ * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character
+ * that is not a valid base64 character (INVALID_BASE64_CHARACTER).
+ *
+ * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input
+ * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then
+ * r.count contains the number of bytes decoded.
+ *
+ * You should call this function with a buffer that is at least maximal_binary_length_from_base64(input, length) bytes long.
+ * If you fail to provide that much space, the function may cause a buffer overflow.
+ *
+ * @param input the base64 string to process
+ * @param length the length of the string in bytes
+ * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long).
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in bytes) if any, or the number of bytes written if successful.
+ */
+simdutf_warn_unused result base64_to_binary(const char * input, size_t length, char* output, base64_options options = base64_default) noexcept;
+
+/**
+ * Provide the base64 length in bytes given the length of a binary input.
+ *
+ * @param length the length of the input in bytes
+ * @return number of base64 bytes
+ */
+simdutf_warn_unused size_t base64_length_from_binary(size_t length, base64_options options = base64_default) noexcept;
+
+/**
+ * Convert a binary input to a base64 ouput. The output is always padded with equal signs so that it is
+ * a multiple of 4 bytes long.
+ *
+ * This function always succeeds.
+ *
+ * @param input the binary to process
+ * @param length the length of the input in bytes
+ * @param output the pointer to buffer that can hold the conversion result (should be at least base64_length_from_binary(length) bytes long)
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return number of written bytes, will be equal to base64_length_from_binary(length, options)
+ */
+size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options = base64_default) noexcept;
+
+/**
+ * Convert a base64 input to a binary ouput.
+ *
+ * This function follows the WHATWG forgiving-base64 format, which means that it will
+ * ignore any ASCII spaces in the input. You may provide a padded input (with one or two
+ * equal signs at the end) or an unpadded input (without any equal signs at the end).
+ *
+ * See https://infra.spec.whatwg.org/#forgiving-base64-decode
+ *
+ * This function will fail in case of invalid input. There are two possible reasons for
+ * failure: the input contains a number of base64 characters that when divided by 4, leaves
+ * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character
+ * that is not a valid base64 character (INVALID_BASE64_CHARACTER).
+ *
+ * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input
+ * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then
+ * r.count contains the number of bytes decoded.
+ *
+ * You should call this function with a buffer that is at least maximal_binary_length_from_utf6_base64(input, length) bytes long.
+ * If you fail to provide that much space, the function may cause a buffer overflow.
+ *
+ * @param input the base64 string to process, in ASCII stored as 16-bit units
+ * @param length the length of the string in 16-bit units
+ * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long).
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of bytes written if successful.
+ */
+simdutf_warn_unused result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options = base64_default) noexcept;
+
+/**
+ * Convert a base64 input to a binary ouput.
+ *
+ * This function follows the WHATWG forgiving-base64 format, which means that it will
+ * ignore any ASCII spaces in the input. You may provide a padded input (with one or two
+ * equal signs at the end) or an unpadded input (without any equal signs at the end).
+ *
+ * See https://infra.spec.whatwg.org/#forgiving-base64-decode
+ *
+ * This function will fail in case of invalid input. There are three possible reasons for
+ * failure: the input contains a number of base64 characters that when divided by 4, leaves
+ * a single remainder character (BASE64_INPUT_REMAINDER), the input contains a character
+ * that is not a valid base64 character (INVALID_BASE64_CHARACTER), or the output buffer
+ * is too small (OUTPUT_BUFFER_TOO_SMALL).
+ *
+ * When OUTPUT_BUFFER_TOO_SMALL, we return both the number of bytes written
+ * and the number of units processed, see description of the parameters and returned value.
+ *
+ * When the error is INVALID_BASE64_CHARACTER, r.count contains the index in the input
+ * where the invalid character was found. When the error is BASE64_INPUT_REMAINDER, then
+ * r.count contains the number of bytes decoded.
+ *
+ * The INVALID_BASE64_CHARACTER cases are considered fatal and you are expected to discard
+ * the output.
+ *
+ * @param input the base64 string to process, in ASCII stored as 8-bit or 16-bit units
+ * @param length the length of the string in 8-bit or 16-bit units.
+ * @param output the pointer to buffer that can hold the conversion result.
+ * @param outlen the number of bytes that can be written in the output buffer. Upon return, it is modified to reflect how many bytes were written.
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of units processed if successful.
+ */
+simdutf_warn_unused result base64_to_binary_safe(const char * input, size_t length, char* output, size_t& outlen, base64_options options = base64_default) noexcept;
+simdutf_warn_unused result base64_to_binary_safe(const char16_t * input, size_t length, char* output, size_t& outlen, base64_options options = base64_default) noexcept;
+
+/**
+ * An implementation of simdutf for a particular CPU architecture.
+ *
+ * Also used to maintain the currently active implementation. The active implementation is
+ * automatically initialized on first use to the most advanced implementation supported by the host.
+ */
+class implementation {
+public:
+
+ /**
+ * The name of this implementation.
+ *
+ * const implementation *impl = simdutf::active_implementation;
+ * cout << "simdutf is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
+ *
+ * @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
+ */
+ virtual std::string name() const { return std::string(_name); }
+
+ /**
+ * The description of this implementation.
+ *
+ * const implementation *impl = simdutf::active_implementation;
+ * cout << "simdutf is optimized for " << impl->name() << "(" << impl->description() << ")" << endl;
+ *
+ * @return the name of the implementation, e.g. "haswell", "westmere", "arm64"
+ */
+ virtual std::string description() const { return std::string(_description); }
+
+ /**
+ * The instruction sets this implementation is compiled against
+ * and the current CPU match. This function may poll the current CPU/system
+ * and should therefore not be called too often if performance is a concern.
+ *
+ *
+ * @return true if the implementation can be safely used on the current system (determined at runtime)
+ */
+ bool supported_by_runtime_system() const;
+
+ /**
+ * This function will try to detect the encoding
+ * @param input the string to identify
+ * @param length the length of the string in bytes.
+ * @return the encoding type detected
+ */
+ virtual encoding_type autodetect_encoding(const char * input, size_t length) const noexcept;
+
+ /**
+ * This function will try to detect the possible encodings in one pass
+ * @param input the string to identify
+ * @param length the length of the string in bytes.
+ * @return the encoding type detected
+ */
+ virtual int detect_encodings(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * @private For internal implementation use
+ *
+ * The instruction sets this implementation is compiled against.
+ *
+ * @return a mask of all required `internal::instruction_set::` values
+ */
+ virtual uint32_t required_instruction_sets() const { return _required_instruction_sets; }
+
+
+ /**
+ * Validate the UTF-8 string.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the UTF-8 string to validate.
+ * @param len the length of the string in bytes.
+ * @return true if and only if the string is valid UTF-8.
+ */
+ simdutf_warn_unused virtual bool validate_utf8(const char *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-8 string and stop on errors.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the UTF-8 string to validate.
+ * @param len the length of the string in bytes.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result validate_utf8_with_errors(const char *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the ASCII string.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the ASCII string to validate.
+ * @param len the length of the string in bytes.
+ * @return true if and only if the string is valid ASCII.
+ */
+ simdutf_warn_unused virtual bool validate_ascii(const char *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the ASCII string and stop on error.
+ *
+ * Overridden by each implementation.
+ *
+ * @param buf the ASCII string to validate.
+ * @param len the length of the string in bytes.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result validate_ascii_with_errors(const char *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-16LE string.This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf16le_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16LE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return true if and only if the string is valid UTF-16LE.
+ */
+ simdutf_warn_unused virtual bool validate_utf16le(const char16_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-16BE string. This function may be best when you expect
+ * the input to be almost always valid. Otherwise, consider using
+ * validate_utf16be_with_errors.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16BE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return true if and only if the string is valid UTF-16BE.
+ */
+ simdutf_warn_unused virtual bool validate_utf16be(const char16_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-16LE string and stop on error. It might be faster than
+ * validate_utf16le when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16LE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result validate_utf16le_with_errors(const char16_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-16BE string and stop on error. It might be faster than
+ * validate_utf16be when an error is expected to occur early.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-16BE string to validate.
+ * @param len the length of the string in number of 2-byte code units (char16_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result validate_utf16be_with_errors(const char16_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-32 string.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-32 string to validate.
+ * @param len the length of the string in number of 4-byte code units (char32_t).
+ * @return true if and only if the string is valid UTF-32.
+ */
+ simdutf_warn_unused virtual bool validate_utf32(const char32_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Validate the UTF-32 string and stop on error.
+ *
+ * Overridden by each implementation.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param buf the UTF-32 string to validate.
+ * @param len the length of the string in number of 4-byte code units (char32_t).
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result validate_utf32_with_errors(const char32_t *buf, size_t len) const noexcept = 0;
+
+ /**
+ * Convert Latin1 string into UTF8 string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_latin1_to_utf8(const char * input, size_t length, char* utf8_output) const noexcept = 0;
+
+
+ /**
+ * Convert possibly Latin1 string into UTF-16LE string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_latin1_to_utf16le(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert Latin1 string into UTF-16BE string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_latin1_to_utf16be(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert Latin1 string into UTF-32 string.
+ *
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char32_t; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_latin1_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if the input was not valid UTF-8 string or if it cannot be represented as Latin1
+ */
+ simdutf_warn_unused virtual size_t convert_utf8_to_latin1(const char * input, size_t length, char* latin1_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into latin1 string with errors.
+ * If the string cannot be represented as Latin1, an error
+ * code is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf8_to_latin1_with_errors(const char * input, size_t length, char* latin1_output) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-8 string into latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param latin1_output the pointer to buffer that can hold conversion result
+ * @return the number of written char; 0 if the input was not valid UTF-8 string
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf8_to_latin1(const char * input, size_t length, char* latin1_output) const noexcept = 0;
+
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-16LE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-16BE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-16LE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf8_to_utf16le_with_errors(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-16BE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of code units validated if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf8_to_utf16be_with_errors(const char * input, size_t length, char16_t* utf16_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t; 0 if the input was not valid UTF-8 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_output) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-8 string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf8_to_utf32_with_errors(const char * input, size_t length, char32_t* utf32_output) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-8 string into UTF-16LE string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf16le(const char * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+/**
+ * Convert valid UTF-8 string into UTF-16BE string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char16_t
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf16be(const char * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-8 string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in bytes
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return the number of written char32_t
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf8_to_utf32(const char * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Compute the number of 2-byte code units that this UTF-8 string would require in UTF-16LE format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return the number of char16_t code units required to encode the UTF-8 string as UTF-16LE
+ */
+ simdutf_warn_unused virtual size_t utf16_length_from_utf8(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of 4-byte code units that this UTF-8 string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf8.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return the number of char32_t code units required to encode the UTF-8 string as UTF-32
+ */
+ simdutf_warn_unused virtual size_t utf32_length_from_utf8(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string or if it cannot be represented as Latin1
+ */
+ simdutf_warn_unused virtual size_t convert_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16BE string or if it cannot be represented as Latin1
+ */
+ simdutf_warn_unused virtual size_t convert_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into Latin1 string.
+ * If the string cannot be represented as Latin1, an error
+ * is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16le_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into Latin1 string.
+ * If the string cannot be represented as Latin1, an error
+ * is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16be_to_latin1_with_errors(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16LE string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16le_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16BE string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16be_to_latin1(const char16_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+ simdutf_warn_unused virtual size_t convert_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16BE string
+ */
+ simdutf_warn_unused virtual size_t convert_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16le_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16be_to_utf8_with_errors(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16LE string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16le_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16BE string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16be_to_utf8(const char16_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16LE string
+ */
+ simdutf_warn_unused virtual size_t convert_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into UTF-32 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-16BE string
+ */
+ simdutf_warn_unused virtual size_t convert_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16LE string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16le_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-16BE string into UTF-32 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char32_t written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf16be_to_utf32_with_errors(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16LE string into UTF-32 string.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16le_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-16LE string into UTF-32BE string.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param utf32_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf16be_to_utf32(const char16_t * input, size_t length, char32_t* utf32_buffer) const noexcept = 0;
+
+ /**
+ * Compute the number of bytes that this UTF-16LE string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-8
+ */
+ simdutf_warn_unused virtual size_t utf8_length_from_utf16le(const char16_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of bytes that this UTF-16BE string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16BE string as UTF-8
+ */
+ simdutf_warn_unused virtual size_t utf8_length_from_utf16be(const char16_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into Latin1 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+
+ simdutf_warn_unused virtual size_t convert_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into Latin1 string and stop on error.
+ * If the string cannot be represented as Latin1, an error is returned.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf32_to_latin1_with_errors(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-32 string into Latin1 string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param latin1_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf32_to_latin1(const char32_t * input, size_t length, char* latin1_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-8 string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-8 string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf32_to_utf8_with_errors(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-32 string into UTF-8 string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf8_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf8(const char32_t * input, size_t length, char* utf8_buffer) const noexcept = 0;
+
+
+ /**
+ * Return the number of bytes that this UTF-16 string would require in Latin1 format.
+ *
+ *
+ * @param input the UTF-16 string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16 string as Latin1
+ */
+ simdutf_warn_unused virtual size_t utf16_length_from_latin1(size_t length) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-16LE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-16BE string.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return number of written code units; 0 if input is not a valid UTF-32 string
+ */
+ simdutf_warn_unused virtual size_t convert_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-16LE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf32_to_utf16le_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert possibly broken UTF-32 string into UTF-16BE string and stop on error.
+ *
+ * During the conversion also validation of the input string is done.
+ * This function is suitable to work with inputs from untrusted sources.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold conversion result
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in code units) if any, or the number of char16_t written if successful.
+ */
+ simdutf_warn_unused virtual result convert_utf32_to_utf16be_with_errors(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-32 string into UTF-16LE string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf16le(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Convert valid UTF-32 string into UTF-16BE string.
+ *
+ * This function assumes that the input string is valid UTF-32.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @param utf16_buffer the pointer to buffer that can hold the conversion result
+ * @return number of written code units; 0 if conversion is not possible
+ */
+ simdutf_warn_unused virtual size_t convert_valid_utf32_to_utf16be(const char32_t * input, size_t length, char16_t* utf16_buffer) const noexcept = 0;
+
+ /**
+ * Change the endianness of the input. Can be used to go from UTF-16LE to UTF-16BE or
+ * from UTF-16BE to UTF-16LE.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16 string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @param output the pointer to buffer that can hold the conversion result
+ */
+ virtual void change_endianness_utf16(const char16_t * input, size_t length, char16_t * output) const noexcept = 0;
+
+ /**
+ * Return the number of bytes that this Latin1 string would require in UTF-8 format.
+ *
+ * @param input the Latin1 string to convert
+ * @param length the length of the string bytes
+ * @return the number of bytes required to encode the Latin1 string as UTF-8
+ */
+ simdutf_warn_unused virtual size_t utf8_length_from_latin1(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of bytes that this UTF-32 string would require in UTF-8 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as UTF-8
+ */
+ simdutf_warn_unused virtual size_t utf8_length_from_utf32(const char32_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of bytes that this UTF-32 string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as Latin1
+ */
+ simdutf_warn_unused virtual size_t latin1_length_from_utf32(size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of bytes that this UTF-8 string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-8 string to convert
+ * @param length the length of the string in byte
+ * @return the number of bytes required to encode the UTF-8 string as Latin1
+ */
+ simdutf_warn_unused virtual size_t latin1_length_from_utf8(const char * input, size_t length) const noexcept = 0;
+
+ /*
+ * Compute the number of bytes that this UTF-16LE/BE string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as Latin1
+ */
+ simdutf_warn_unused virtual size_t latin1_length_from_utf16(size_t length) const noexcept = 0;
+
+ /**
+ * Compute the number of two-byte code units that this UTF-32 string would require in UTF-16 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as UTF-16
+ */
+ simdutf_warn_unused virtual size_t utf16_length_from_utf32(const char32_t * input, size_t length) const noexcept = 0;
+
+
+ /**
+ * Return the number of bytes that this UTF-32 string would require in Latin1 format.
+ *
+ * This function does not validate the input.
+ *
+ * @param input the UTF-32 string to convert
+ * @param length the length of the string in 4-byte code units (char32_t)
+ * @return the number of bytes required to encode the UTF-32 string as Latin1
+ */
+ simdutf_warn_unused virtual size_t utf32_length_from_latin1(size_t length) const noexcept = 0;
+
+ /*
+ * Compute the number of bytes that this UTF-16LE string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf16le.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16LE string as UTF-32
+ */
+ simdutf_warn_unused virtual size_t utf32_length_from_utf16le(const char16_t * input, size_t length) const noexcept = 0;
+
+ /*
+ * Compute the number of bytes that this UTF-16BE string would require in UTF-32 format.
+ *
+ * This function is equivalent to count_utf16be.
+ *
+ * This function does not validate the input.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to convert
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return the number of bytes required to encode the UTF-16BE string as UTF-32
+ */
+ simdutf_warn_unused virtual size_t utf32_length_from_utf16be(const char16_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-16LE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16LE string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return number of code points
+ */
+ simdutf_warn_unused virtual size_t count_utf16le(const char16_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-16BE.
+ *
+ * This function is not BOM-aware.
+ *
+ * @param input the UTF-16BE string to process
+ * @param length the length of the string in 2-byte code units (char16_t)
+ * @return number of code points
+ */
+ simdutf_warn_unused virtual size_t count_utf16be(const char16_t * input, size_t length) const noexcept = 0;
+
+
+ /**
+ * Count the number of code points (characters) in the string assuming that
+ * it is valid.
+ *
+ * This function assumes that the input string is valid UTF-8.
+ *
+ * @param input the UTF-8 string to process
+ * @param length the length of the string in bytes
+ * @return number of code points
+ */
+ simdutf_warn_unused virtual size_t count_utf8(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * Provide the maximal binary length in bytes given the base64 input.
+ * In general, if the input contains ASCII spaces, the result will be less than
+ * the maximum length.
+ *
+ * @param input the base64 input to process
+ * @param length the length of the base64 input in bytes
+ * @return maximal number of binary bytes
+ */
+ simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char * input, size_t length) const noexcept = 0;
+
+ /**
+ * Provide the maximal binary length in bytes given the base64 input.
+ * In general, if the input contains ASCII spaces, the result will be less than
+ * the maximum length.
+ *
+ * @param input the base64 input to process, in ASCII stored as 16-bit units
+ * @param length the length of the base64 input in 16-bit units
+ * @return maximal number of binary bytes
+ */
+ simdutf_warn_unused virtual size_t maximal_binary_length_from_base64(const char16_t * input, size_t length) const noexcept = 0;
+
+ /**
+ * Convert a base64 input to a binary ouput.
+ *
+ * This function follows the WHATWG forgiving-base64 format, which means that it will
+ * ignore any ASCII spaces in the input. You may provide a padded input (with one or two
+ * equal signs at the end) or an unpadded input (without any equal signs at the end).
+ *
+ * See https://infra.spec.whatwg.org/#forgiving-base64-decode
+ *
+ * This function will fail in case of invalid input. There are two possible reasons for
+ * failure: the input contains a number of base64 characters that when divided by 4, leaves
+ * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character
+ * that is not a valid base64 character (INVALID_BASE64_CHARACTER).
+ *
+ * You should call this function with a buffer that is at least maximal_binary_length_from_base64(input, length) bytes long.
+ * If you fail to provide that much space, the function may cause a buffer overflow.
+ *
+ * @param input the base64 string to process
+ * @param length the length of the string in bytes
+ * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long).
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and either position of the error (in the input in bytes) if any, or the number of bytes written if successful.
+ */
+ simdutf_warn_unused virtual result base64_to_binary(const char * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0;
+
+ /**
+ * Convert a base64 input to a binary ouput.
+ *
+ * This function follows the WHATWG forgiving-base64 format, which means that it will
+ * ignore any ASCII spaces in the input. You may provide a padded input (with one or two
+ * equal signs at the end) or an unpadded input (without any equal signs at the end).
+ *
+ * See https://infra.spec.whatwg.org/#forgiving-base64-decode
+ *
+ * This function will fail in case of invalid input. There are two possible reasons for
+ * failure: the input contains a number of base64 characters that when divided by 4, leaves
+ * a single remainder character (BASE64_INPUT_REMAINDER), or the input contains a character
+ * that is not a valid base64 character (INVALID_BASE64_CHARACTER).
+ *
+ * You should call this function with a buffer that is at least maximal_binary_length_from_utf6_base64(input, length) bytes long.
+ * If you fail to provide that much space, the function may cause a buffer overflow.
+ *
+ * @param input the base64 string to process, in ASCII stored as 16-bit units
+ * @param length the length of the string in 16-bit units
+ * @param output the pointer to buffer that can hold the conversion result (should be at least maximal_binary_length_from_base64(input, length) bytes long).
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return a result pair struct (of type simdutf::error containing the two fields error and count) with an error code and position of the INVALID_BASE64_CHARACTER error (in the input in units) if any, or the number of bytes written if successful.
+ */
+ simdutf_warn_unused virtual result base64_to_binary(const char16_t * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0;
+
+ /**
+ * Provide the base64 length in bytes given the length of a binary input.
+ *
+ * @param length the length of the input in bytes
+ * @parem options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return number of base64 bytes
+ */
+ simdutf_warn_unused virtual size_t base64_length_from_binary(size_t length, base64_options options = base64_default) const noexcept = 0;
+
+ /**
+ * Convert a binary input to a base64 ouput. The output is always padded with equal signs so that it is
+ * a multiple of 4 bytes long.
+ *
+ * This function always succeeds.
+ *
+ * @param input the binary to process
+ * @param length the length of the input in bytes
+ * @param output the pointer to buffer that can hold the conversion result (should be at least base64_length_from_binary(length) bytes long)
+ * @param options the base64 options to use, can be base64_default or base64_url, is base64_default by default.
+ * @return number of written bytes, will be equal to base64_length_from_binary(length, options)
+ */
+ virtual size_t binary_to_base64(const char * input, size_t length, char* output, base64_options options = base64_default) const noexcept = 0;
+
+
+protected:
+ /** @private Construct an implementation with the given name and description. For subclasses. */
+ simdutf_really_inline implementation(
+ const char* name,
+ const char* description,
+ uint32_t required_instruction_sets
+ ) :
+ _name(name),
+ _description(description),
+ _required_instruction_sets(required_instruction_sets)
+ {
+ }
+protected:
+ ~implementation() = default;
+private:
+ /**
+ * The name of this implementation.
+ */
+ const char* _name;
+
+ /**
+ * The description of this implementation.
+ */
+ const char* _description;
+
+ /**
+ * Instruction sets required for this implementation.
+ */
+ const uint32_t _required_instruction_sets;
+};
+
+/** @private */
+namespace internal {
+
+/**
+ * The list of available implementations compiled into simdutf.
+ */
+class available_implementation_list {
+public:
+ /** Get the list of available implementations compiled into simdutf */
+ simdutf_really_inline available_implementation_list() {}
+ /** Number of implementations */
+ size_t size() const noexcept;
+ /** STL const begin() iterator */
+ const implementation * const *begin() const noexcept;
+ /** STL const end() iterator */
+ const implementation * const *end() const noexcept;
+
+ /**
+ * Get the implementation with the given name.
+ *
+ * Case sensitive.
+ *
+ * const implementation *impl = simdutf::available_implementations["westmere"];
+ * if (!impl) { exit(1); }
+ * if (!imp->supported_by_runtime_system()) { exit(1); }
+ * simdutf::active_implementation = impl;
+ *
+ * @param name the implementation to find, e.g. "westmere", "haswell", "arm64"
+ * @return the implementation, or nullptr if the parse failed.
+ */
+ const implementation * operator[](const std::string &name) const noexcept {
+ for (const implementation * impl : *this) {
+ if (impl->name() == name) { return impl; }
+ }
+ return nullptr;
+ }
+
+ /**
+ * Detect the most advanced implementation supported by the current host.
+ *
+ * This is used to initialize the implementation on startup.
+ *
+ * const implementation *impl = simdutf::available_implementation::detect_best_supported();
+ * simdutf::active_implementation = impl;
+ *
+ * @return the most advanced supported implementation for the current host, or an
+ * implementation that returns UNSUPPORTED_ARCHITECTURE if there is no supported
+ * implementation. Will never return nullptr.
+ */
+ const implementation *detect_best_supported() const noexcept;
+};
+
+template<typename T>
+class atomic_ptr {
+public:
+ atomic_ptr(T *_ptr) : ptr{_ptr} {}
+
+#if defined(SIMDUTF_NO_THREADS)
+ operator const T*() const { return ptr; }
+ const T& operator*() const { return *ptr; }
+ const T* operator->() const { return ptr; }
+
+ operator T*() { return ptr; }
+ T& operator*() { return *ptr; }
+ T* operator->() { return ptr; }
+ atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; }
+
+#else
+ operator const T*() const { return ptr.load(); }
+ const T& operator*() const { return *ptr; }
+ const T* operator->() const { return ptr.load(); }
+
+ operator T*() { return ptr.load(); }
+ T& operator*() { return *ptr; }
+ T* operator->() { return ptr.load(); }
+ atomic_ptr& operator=(T *_ptr) { ptr = _ptr; return *this; }
+
+#endif
+
+private:
+#if defined(SIMDUTF_NO_THREADS)
+ T* ptr;
+#else
+ std::atomic<T*> ptr;
+#endif
+};
+
+class detect_best_supported_implementation_on_first_use;
+
+} // namespace internal
+
+/**
+ * The list of available implementations compiled into simdutf.
+ */
+extern SIMDUTF_DLLIMPORTEXPORT const internal::available_implementation_list& get_available_implementations();
+
+/**
+ * The active implementation.
+ *
+ * Automatically initialized on first use to the most advanced implementation supported by this hardware.
+ */
+extern SIMDUTF_DLLIMPORTEXPORT internal::atomic_ptr<const implementation>& get_active_implementation();
+
+
+} // namespace simdutf
+
+#endif // SIMDUTF_IMPLEMENTATION_H
+/* end file include/simdutf/implementation.h */
+
+
+// Implementation-internal files (must be included before the implementations themselves, to keep
+// amalgamation working--otherwise, the first time a file is included, it might be put inside the
+// #ifdef SIMDUTF_IMPLEMENTATION_ARM64/FALLBACK/etc., which means the other implementations can't
+// compile unless that implementation is turned on).
+
+
+SIMDUTF_POP_DISABLE_WARNINGS
+
+#endif // SIMDUTF_H
+/* end file include/simdutf.h */
diff --git a/vendor/libghostty-vt/pkg/spirv-cross/build.zig b/vendor/libghostty-vt/pkg/spirv-cross/build.zig
new file mode 100644
index 00000000..72ce61eb
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/spirv-cross/build.zig
@@ -0,0 +1,120 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const module = b.addModule("spirv_cross", .{ .root_source_file = b.path("main.zig"), .target = target, .optimize = optimize });
+
+ // For dynamic linking, we prefer dynamic linking and to search by
+ // mode first. Mode first will search all paths for a dynamic library
+ // before falling back to static.
+ const dynamic_link_opts: std.Build.Module.LinkSystemLibraryOptions = .{
+ .preferred_link_mode = .dynamic,
+ .search_strategy = .mode_first,
+ };
+
+ var test_exe: ?*std.Build.Step.Compile = null;
+ if (target.query.isNative()) {
+ test_exe = b.addTest(.{
+ .name = "test",
+ .root_module = b.createModule(.{
+ .root_source_file = b.path("main.zig"),
+ .target = target,
+ .optimize = optimize,
+ }),
+ });
+ const tests_run = b.addRunArtifact(test_exe.?);
+ const test_step = b.step("test", "Run tests");
+ test_step.dependOn(&tests_run.step);
+
+ // Uncomment this if we're debugging tests
+ // b.installArtifact(test_exe.?);
+ }
+ if (b.systemIntegrationOption("spirv-cross", .{})) {
+ module.linkSystemLibrary("spirv-cross-c-shared", dynamic_link_opts);
+ if (test_exe) |exe| {
+ exe.linkSystemLibrary2("spirv-cross-c-shared", dynamic_link_opts);
+ }
+ } else {
+ const lib = try buildSpirvCross(b, module, target, optimize);
+ b.installArtifact(lib);
+ if (test_exe) |exe| exe.linkLibrary(lib);
+ }
+}
+
+fn buildSpirvCross(
+ b: *std.Build,
+ module: *std.Build.Module,
+ target: std.Build.ResolvedTarget,
+ optimize: std.builtin.OptimizeMode,
+) !*std.Build.Step.Compile {
+ const lib = b.addLibrary(.{
+ .name = "spirv_cross",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DSPIRV_CROSS_C_API_GLSL=1",
+ "-DSPIRV_CROSS_C_API_MSL=1",
+
+ "-fno-sanitize=undefined",
+ "-fno-sanitize-trap=undefined",
+ });
+
+ if (target.result.os.tag == .freebsd or target.result.abi == .musl) {
+ try flags.append(b.allocator, "-fPIC");
+ }
+
+ if (b.lazyDependency("spirv_cross", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ module.addIncludePath(upstream.path(""));
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .flags = flags.items,
+ .files = &.{
+ // Core
+ "spirv_cross.cpp",
+ "spirv_parser.cpp",
+ "spirv_cross_parsed_ir.cpp",
+ "spirv_cfg.cpp",
+
+ // C
+ "spirv_cross_c.cpp",
+
+ // GLSL
+ "spirv_glsl.cpp",
+
+ // MSL
+ "spirv_msl.cpp",
+ },
+ });
+
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ return lib;
+}
diff --git a/vendor/libghostty-vt/pkg/spirv-cross/build.zig.zon b/vendor/libghostty-vt/pkg/spirv-cross/build.zig.zon
new file mode 100644
index 00000000..30eea950
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/spirv-cross/build.zig.zon
@@ -0,0 +1,16 @@
+.{
+ .name = .spirv_cross,
+ .version = "13.1.1",
+ .fingerprint = 0x7ea1d8312b06cca,
+ .paths = .{""},
+ .dependencies = .{
+ // KhronosGroup/SPIRV-Cross
+ .spirv_cross = .{
+ .url = "https://deps.files.ghostty.org/spirv_cross-1220fb3b5586e8be67bc3feb34cbe749cf42a60d628d2953632c2f8141302748c8da.tar.gz",
+ .hash = "N-V-__8AANb6pwD7O1WG6L5nvD_rNMvnSc9Cpg1ijSlTYywv",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/spirv-cross/c.zig b/vendor/libghostty-vt/pkg/spirv-cross/c.zig
new file mode 100644
index 00000000..08a999a3
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/spirv-cross/c.zig
@@ -0,0 +1,3 @@
+pub const c = @cImport({
+ @cInclude("spirv_cross_c.h");
+});
diff --git a/vendor/libghostty-vt/pkg/spirv-cross/main.zig b/vendor/libghostty-vt/pkg/spirv-cross/main.zig
new file mode 100644
index 00000000..1ef5493b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/spirv-cross/main.zig
@@ -0,0 +1 @@
+pub const c = @import("c.zig").c;
diff --git a/vendor/libghostty-vt/pkg/utfcpp/build.zig b/vendor/libghostty-vt/pkg/utfcpp/build.zig
new file mode 100644
index 00000000..15c652c1
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/utfcpp/build.zig
@@ -0,0 +1,69 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addLibrary(.{
+ .name = "utfcpp",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ // On MSVC, we must not use linkLibCpp because Zig unconditionally
+ // passes -nostdinc++ and then adds its bundled libc++/libc++abi
+ // include paths, which conflict with MSVC's own C++ runtime headers.
+ // The MSVC SDK include directories (added via linkLibC) contain
+ // both C and C++ headers, so linkLibCpp is not needed.
+ if (target.result.abi != .msvc) {
+ lib.linkLibCpp();
+ }
+
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (target.result.abi.isAndroid()) {
+ const android_ndk = @import("android_ndk");
+ try android_ndk.addPaths(b, lib);
+ }
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+
+ lib.addCSourceFiles(.{
+ .flags = flags.items,
+ .files = &.{"empty.cc"},
+ });
+
+ if (b.lazyDependency("utfcpp", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ lib.installHeadersDirectory(
+ upstream.path("source"),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+ }
+
+ b.installArtifact(lib);
+
+ // {
+ // const test_exe = b.addTest(.{
+ // .name = "test",
+ // .root_source_file = .{ .path = "main.zig" },
+ // .target = target,
+ // .optimize = optimize,
+ // });
+ // test_exe.linkLibrary(lib);
+ //
+ // var it = module.import_table.iterator();
+ // while (it.next()) |entry| test_exe.root_module.addImport(entry.key_ptr.*, entry.value_ptr.*);
+ // const tests_run = b.addRunArtifact(test_exe);
+ // const test_step = b.step("test", "Run tests");
+ // test_step.dependOn(&tests_run.step);
+ // }
+}
diff --git a/vendor/libghostty-vt/pkg/utfcpp/build.zig.zon b/vendor/libghostty-vt/pkg/utfcpp/build.zig.zon
new file mode 100644
index 00000000..1077e965
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/utfcpp/build.zig.zon
@@ -0,0 +1,17 @@
+.{
+ .name = .utfcpp,
+ .version = "4.0.5",
+ .fingerprint = 0xcd99aeb2334ae11a,
+ .paths = .{""},
+ .dependencies = .{
+ // nemtrif/utfcpp
+ .utfcpp = .{
+ .url = "https://deps.files.ghostty.org/utfcpp-1220d4d18426ca72fc2b7e56ce47273149815501d0d2395c2a98c726b31ba931e641.tar.gz",
+ .hash = "N-V-__8AAHffAgDU0YQmynL8K35WzkcnMUmBVQHQ0jlcKpjH",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ .android_ndk = .{ .path = "../android-ndk" },
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/utfcpp/empty.cc b/vendor/libghostty-vt/pkg/utfcpp/empty.cc
new file mode 100644
index 00000000..f2ef7409
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/utfcpp/empty.cc
@@ -0,0 +1,2 @@
+// Needed for Zig build to be happy
+void ghostty_utfcpp_stub() {}
diff --git a/vendor/libghostty-vt/pkg/wuffs/build.zig b/vendor/libghostty-vt/pkg/wuffs/build.zig
new file mode 100644
index 00000000..3d9f83da
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/build.zig
@@ -0,0 +1,53 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const module = b.addModule("wuffs", .{
+ .root_source_file = b.path("src/main.zig"),
+ .target = target,
+ .optimize = optimize,
+ .link_libc = true,
+ });
+
+ const unit_tests = b.addTest(.{
+ .name = "test",
+ .root_module = module,
+ });
+ unit_tests.linkLibC();
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.append(b.allocator, "-DWUFFS_IMPLEMENTATION");
+ inline for (@import("src/c.zig").defines) |key| {
+ try flags.append(b.allocator, "-D" ++ key);
+ }
+
+ if (b.lazyDependency("wuffs", .{})) |wuffs_dep| {
+ module.addIncludePath(wuffs_dep.path("release/c"));
+ module.addCSourceFile(.{
+ .file = wuffs_dep.path("release/c/wuffs-v0.4.c"),
+ .flags = flags.items,
+ });
+ }
+
+ if (b.lazyDependency("pixels", .{})) |pixels_dep| {
+ inline for (.{ "000000", "FFFFFF" }) |color| {
+ inline for (.{ "gif", "jpg", "png", "ppm" }) |extension| {
+ const filename = std.fmt.comptimePrint(
+ "1x1#{s}.{s}",
+ .{ color, extension },
+ );
+ unit_tests.root_module.addAnonymousImport(filename, .{
+ .root_source_file = pixels_dep.path(filename),
+ });
+ }
+ }
+ }
+
+ const run_unit_tests = b.addRunArtifact(unit_tests);
+
+ const test_step = b.step("test", "Run unit tests");
+ test_step.dependOn(&run_unit_tests.step);
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/build.zig.zon b/vendor/libghostty-vt/pkg/wuffs/build.zig.zon
new file mode 100644
index 00000000..eb99ba0b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/build.zig.zon
@@ -0,0 +1,27 @@
+.{
+ .name = .wuffs,
+ .version = "0.0.0",
+ .fingerprint = 0x67c0c059de921c4f,
+ .dependencies = .{
+ // google/wuffs
+ .wuffs = .{
+ .url = "https://deps.files.ghostty.org/wuffs-122037b39d577ec2db3fd7b2130e7b69ef6cc1807d68607a7c232c958315d381b5cd.tar.gz",
+ .hash = "N-V-__8AAAzZywE3s51XfsLbP9eyEw57ae9swYB9aGB6fCMs",
+ .lazy = true,
+ },
+
+ // make-github-pseudonymous-again/pixels
+ .pixels = .{
+ .url = "https://deps.files.ghostty.org/pixels-12207ff340169c7d40c570b4b6a97db614fe47e0d83b5801a932dcd44917424c8806.tar.gz",
+ .hash = "N-V-__8AADYiAAB_80AWnH1AxXC0tql9thT-R-DYO1gBqTLc",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+ .paths = .{
+ "build.zig",
+ "build.zig.zon",
+ "src",
+ },
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/c.zig b/vendor/libghostty-vt/pkg/wuffs/src/c.zig
new file mode 100644
index 00000000..d94247df
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/c.zig
@@ -0,0 +1,18 @@
+pub const c = @cImport({
+ for (defines) |d| @cDefine(d, "1");
+ @cInclude("wuffs-v0.4.c");
+});
+
+/// All the C macros defined so that the header matches the build.
+pub const defines: []const []const u8 = &[_][]const u8{
+ "WUFFS_CONFIG__MODULES",
+ "WUFFS_CONFIG__MODULE__AUX__BASE",
+ "WUFFS_CONFIG__MODULE__AUX__IMAGE",
+ "WUFFS_CONFIG__MODULE__BASE",
+ "WUFFS_CONFIG__MODULE__ADLER32",
+ "WUFFS_CONFIG__MODULE__CRC32",
+ "WUFFS_CONFIG__MODULE__DEFLATE",
+ "WUFFS_CONFIG__MODULE__JPEG",
+ "WUFFS_CONFIG__MODULE__PNG",
+ "WUFFS_CONFIG__MODULE__ZLIB",
+};
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/error.zig b/vendor/libghostty-vt/pkg/wuffs/src/error.zig
new file mode 100644
index 00000000..0be55cf4
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/error.zig
@@ -0,0 +1,13 @@
+const std = @import("std");
+
+const c = @import("c.zig").c;
+
+pub const Error = std.mem.Allocator.Error || error{ WuffsError, Overflow };
+
+pub fn check(log: anytype, status: *const c.struct_wuffs_base__status__struct) error{WuffsError}!void {
+ if (!c.wuffs_base__status__is_ok(status)) {
+ const e = c.wuffs_base__status__message(status);
+ log.warn("decode err={s}", .{e});
+ return error.WuffsError;
+ }
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig b/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig
new file mode 100644
index 00000000..69d91c8a
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/jpeg.zig
@@ -0,0 +1,151 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const Error = @import("error.zig").Error;
+const check = @import("error.zig").check;
+const ImageData = @import("main.zig").ImageData;
+const maximum_image_size = @import("main.zig").maximum_image_size;
+const mul = std.math.mul;
+
+const log = std.log.scoped(.wuffs_jpeg);
+
+/// Decode a JPEG image.
+pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
+ // Work around some weirdness in WUFFS/Zig, there are some structs that
+ // are defined as "extern" by the Zig compiler which means that Zig won't
+ // allocate them on the stack at compile time. WUFFS has functions for
+ // dynamically allocating these structs but they use the C malloc/free. This
+ // gets around that by using the Zig allocator to allocate enough memory for
+ // the struct and then casts it to the appropriate pointer.
+
+ const decoder_buf = try alloc.alloc(u8, c.sizeof__wuffs_jpeg__decoder());
+ defer alloc.free(decoder_buf);
+
+ const decoder: ?*c.wuffs_jpeg__decoder = @ptrCast(decoder_buf);
+ {
+ const status = c.wuffs_jpeg__decoder__initialize(
+ decoder,
+ c.sizeof__wuffs_jpeg__decoder(),
+ c.WUFFS_VERSION,
+ 0,
+ );
+ try check(log, &status);
+ }
+
+ var source_buffer: c.wuffs_base__io_buffer = .{
+ .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len },
+ .meta = .{
+ .wi = data.len,
+ .ri = 0,
+ .pos = 0,
+ .closed = true,
+ },
+ };
+
+ var image_config: c.wuffs_base__image_config = undefined;
+ {
+ const status = c.wuffs_jpeg__decoder__decode_image_config(
+ decoder,
+ &image_config,
+ &source_buffer,
+ );
+ try check(log, &status);
+ }
+
+ const width = c.wuffs_base__pixel_config__width(&image_config.pixcfg);
+ const height = c.wuffs_base__pixel_config__height(&image_config.pixcfg);
+
+ c.wuffs_base__pixel_config__set(
+ &image_config.pixcfg,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL,
+ c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
+ width,
+ height,
+ );
+
+ const size: usize = try mul(
+ usize,
+ try mul(usize, width, height),
+ @sizeOf(c.wuffs_base__color_u32_argb_premul),
+ );
+
+ if (size > maximum_image_size) {
+ log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
+ return error.Overflow;
+ }
+
+ const destination = try alloc.alloc(
+ u8,
+ size,
+ );
+ errdefer alloc.free(destination);
+
+ // temporary buffer for intermediate processing of image
+ const work_buffer = try alloc.alloc(
+ u8,
+
+ // The type of this is a u64 on all systems but our allocator
+ // uses a usize which is a u32 on 32-bit systems.
+ std.math.cast(
+ usize,
+ c.wuffs_jpeg__decoder__workbuf_len(decoder).max_incl,
+ ) orelse return error.OutOfMemory,
+ );
+ defer alloc.free(work_buffer);
+
+ const work_slice = c.wuffs_base__make_slice_u8(
+ work_buffer.ptr,
+ work_buffer.len,
+ );
+
+ var pixel_buffer: c.wuffs_base__pixel_buffer = undefined;
+ {
+ const status = c.wuffs_base__pixel_buffer__set_from_slice(
+ &pixel_buffer,
+ &image_config.pixcfg,
+ c.wuffs_base__make_slice_u8(destination.ptr, destination.len),
+ );
+ try check(log, &status);
+ }
+
+ {
+ const status = c.wuffs_jpeg__decoder__decode_frame(
+ decoder,
+ &pixel_buffer,
+ &source_buffer,
+ c.WUFFS_BASE__PIXEL_BLEND__SRC,
+ work_slice,
+ null,
+ );
+ try check(log, &status);
+ }
+
+ return .{
+ .width = width,
+ .height = height,
+ .data = destination,
+ };
+}
+
+test "jpeg_decode_000000" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#000000.jpg"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0, 0, 255 }, data.data);
+}
+
+test "jpeg_decode_FFFFFF" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#FFFFFF.jpg"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
+}
+
+test "jpeg: too big" {
+ const data = decode(std.testing.allocator, @embedFile("too_big.jpg"));
+ try std.testing.expectError(error.Overflow, data);
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/main.zig b/vendor/libghostty-vt/pkg/wuffs/src/main.zig
new file mode 100644
index 00000000..207d83f9
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/main.zig
@@ -0,0 +1,20 @@
+const std = @import("std");
+
+pub const png = @import("png.zig");
+pub const jpeg = @import("jpeg.zig");
+pub const swizzle = @import("swizzle.zig");
+pub const Error = @import("error.zig").Error;
+
+/// The maximum image size, based on the 4G limit of Ghostty's
+/// `image-storage-limit` config.
+pub const maximum_image_size = 4 * 1024 * 1024 * 1024;
+
+pub const ImageData = struct {
+ width: u32,
+ height: u32,
+ data: []u8,
+};
+
+test {
+ std.testing.refAllDeclsRecursive(@This());
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/png.zig b/vendor/libghostty-vt/pkg/wuffs/src/png.zig
new file mode 100644
index 00000000..57a0e63b
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/png.zig
@@ -0,0 +1,151 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const c = @import("c.zig").c;
+const Error = @import("error.zig").Error;
+const check = @import("error.zig").check;
+const ImageData = @import("main.zig").ImageData;
+const maximum_image_size = @import("main.zig").maximum_image_size;
+const mul = std.math.mul;
+
+const log = std.log.scoped(.wuffs_png);
+
+/// Decode a PNG image.
+pub fn decode(alloc: Allocator, data: []const u8) Error!ImageData {
+ // Work around some weirdness in WUFFS/Zig, there are some structs that
+ // are defined as "extern" by the Zig compiler which means that Zig won't
+ // allocate them on the stack at compile time. WUFFS has functions for
+ // dynamically allocating these structs but they use the C malloc/free. This
+ // gets around that by using the Zig allocator to allocate enough memory for
+ // the struct and then casts it to the appropriate pointer.
+
+ const decoder_buf = try alloc.alloc(u8, c.sizeof__wuffs_png__decoder());
+ defer alloc.free(decoder_buf);
+
+ const decoder: ?*c.wuffs_png__decoder = @ptrCast(decoder_buf);
+ {
+ const status = c.wuffs_png__decoder__initialize(
+ decoder,
+ c.sizeof__wuffs_png__decoder(),
+ c.WUFFS_VERSION,
+ 0,
+ );
+ try check(log, &status);
+ }
+
+ var source_buffer: c.wuffs_base__io_buffer = .{
+ .data = .{ .ptr = @ptrCast(@constCast(data.ptr)), .len = data.len },
+ .meta = .{
+ .wi = data.len,
+ .ri = 0,
+ .pos = 0,
+ .closed = true,
+ },
+ };
+
+ var image_config: c.wuffs_base__image_config = undefined;
+ {
+ const status = c.wuffs_png__decoder__decode_image_config(
+ decoder,
+ &image_config,
+ &source_buffer,
+ );
+ try check(log, &status);
+ }
+
+ const width = c.wuffs_base__pixel_config__width(&image_config.pixcfg);
+ const height = c.wuffs_base__pixel_config__height(&image_config.pixcfg);
+
+ c.wuffs_base__pixel_config__set(
+ &image_config.pixcfg,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_NONPREMUL,
+ c.WUFFS_BASE__PIXEL_SUBSAMPLING__NONE,
+ width,
+ height,
+ );
+
+ const size: usize = try mul(
+ usize,
+ try mul(usize, width, height),
+ @sizeOf(c.wuffs_base__color_u32_argb_premul),
+ );
+
+ if (size > maximum_image_size) {
+ log.warn("image size {d} is larger than the maximum allowed ({d})", .{ size, maximum_image_size });
+ return error.Overflow;
+ }
+
+ const destination = try alloc.alloc(
+ u8,
+ size,
+ );
+ errdefer alloc.free(destination);
+
+ // temporary buffer for intermediate processing of image
+ const work_buffer = try alloc.alloc(
+ u8,
+
+ // The type of this is a u64 on all systems but our allocator
+ // uses a usize which is a u32 on 32-bit systems.
+ std.math.cast(
+ usize,
+ c.wuffs_png__decoder__workbuf_len(decoder).max_incl,
+ ) orelse return error.OutOfMemory,
+ );
+ defer alloc.free(work_buffer);
+
+ const work_slice = c.wuffs_base__make_slice_u8(
+ work_buffer.ptr,
+ work_buffer.len,
+ );
+
+ var pixel_buffer: c.wuffs_base__pixel_buffer = undefined;
+ {
+ const status = c.wuffs_base__pixel_buffer__set_from_slice(
+ &pixel_buffer,
+ &image_config.pixcfg,
+ c.wuffs_base__make_slice_u8(destination.ptr, destination.len),
+ );
+ try check(log, &status);
+ }
+
+ {
+ const status = c.wuffs_png__decoder__decode_frame(
+ decoder,
+ &pixel_buffer,
+ &source_buffer,
+ c.WUFFS_BASE__PIXEL_BLEND__SRC,
+ work_slice,
+ null,
+ );
+ try check(log, &status);
+ }
+
+ return .{
+ .width = width,
+ .height = height,
+ .data = destination,
+ };
+}
+
+test "png_decode_000000" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#000000.png"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 0, 0, 0, 255 }, data.data);
+}
+
+test "png_decode_FFFFFF" {
+ const data = try decode(std.testing.allocator, @embedFile("1x1#FFFFFF.png"));
+ defer std.testing.allocator.free(data.data);
+
+ try std.testing.expectEqual(1, data.width);
+ try std.testing.expectEqual(1, data.height);
+ try std.testing.expectEqualSlices(u8, &.{ 255, 255, 255, 255 }, data.data);
+}
+
+test "png: too big" {
+ const data = decode(std.testing.allocator, @embedFile("too_big.png"));
+ try std.testing.expectError(error.Overflow, data);
+}
diff --git a/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig b/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig
new file mode 100644
index 00000000..352cf2b5
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/wuffs/src/swizzle.zig
@@ -0,0 +1,121 @@
+const std = @import("std");
+const Allocator = std.mem.Allocator;
+const assert = std.debug.assert;
+const c = @import("c.zig").c;
+const Error = @import("error.zig").Error;
+
+const log = std.log.scoped(.wuffs_swizzler);
+
+pub fn gToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__Y,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn gaToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__YA_PREMUL,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn rgbToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGB,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn bgrToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__BGR,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+pub fn bgraToRgba(alloc: Allocator, src: []const u8) Error![]u8 {
+ return swizzle(
+ alloc,
+ src,
+ c.WUFFS_BASE__PIXEL_FORMAT__BGRA_PREMUL,
+ c.WUFFS_BASE__PIXEL_FORMAT__RGBA_PREMUL,
+ );
+}
+
+fn swizzle(
+ alloc: Allocator,
+ src: []const u8,
+ comptime src_pixel_format: u32,
+ comptime dst_pixel_format: u32,
+) Error![]u8 {
+ const src_slice = c.wuffs_base__make_slice_u8(
+ @constCast(src.ptr),
+ src.len,
+ );
+
+ const dst_fmt = c.wuffs_base__make_pixel_format(
+ dst_pixel_format,
+ );
+
+ assert(c.wuffs_base__pixel_format__is_direct(&dst_fmt));
+ assert(c.wuffs_base__pixel_format__is_interleaved(&dst_fmt));
+ assert(c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) % 8 == 0);
+
+ const dst_size = c.wuffs_base__pixel_format__bits_per_pixel(&dst_fmt) / 8;
+
+ const src_fmt = c.wuffs_base__make_pixel_format(
+ src_pixel_format,
+ );
+
+ assert(c.wuffs_base__pixel_format__is_direct(&src_fmt));
+ assert(c.wuffs_base__pixel_format__is_interleaved(&src_fmt));
+ assert(c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) % 8 == 0);
+
+ const src_size = c.wuffs_base__pixel_format__bits_per_pixel(&src_fmt) / 8;
+
+ assert(src.len % src_size == 0);
+
+ const dst = try alloc.alloc(u8, src.len * dst_size / src_size);
+ errdefer alloc.free(dst);
+
+ const dst_slice = c.wuffs_base__make_slice_u8(
+ dst.ptr,
+ dst.len,
+ );
+
+ var swizzler: c.wuffs_base__pixel_swizzler = undefined;
+ {
+ const status = c.wuffs_base__pixel_swizzler__prepare(
+ &swizzler,
+ dst_fmt,
+ c.wuffs_base__empty_slice_u8(),
+ src_fmt,
+ c.wuffs_base__empty_slice_u8(),
+ c.WUFFS_BASE__PIXEL_BLEND__SRC,
+ );
+ if (!c.wuffs_base__status__is_ok(&status)) {
+ const e = c.wuffs_base__status__message(&status);
+ log.warn("{s}", .{e});
+ return error.WuffsError;
+ }
+ }
+ {
+ _ = c.wuffs_base__pixel_swizzler__swizzle_interleaved_from_slice(
+ &swizzler,
+ dst_slice,
+ c.wuffs_base__empty_slice_u8(),
+ src_slice,
+ );
+ }
+
+ return dst;
+}
diff --git a/vendor/libghostty-vt/pkg/zlib/build.zig b/vendor/libghostty-vt/pkg/zlib/build.zig
new file mode 100644
index 00000000..6bde60ec
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/zlib/build.zig
@@ -0,0 +1,71 @@
+const std = @import("std");
+
+pub fn build(b: *std.Build) !void {
+ const target = b.standardTargetOptions(.{});
+ const optimize = b.standardOptimizeOption(.{});
+
+ const lib = b.addLibrary(.{
+ .name = "z",
+ .root_module = b.createModule(.{
+ .target = target,
+ .optimize = optimize,
+ }),
+ .linkage = .static,
+ });
+ lib.linkLibC();
+ if (target.result.os.tag.isDarwin()) {
+ const apple_sdk = @import("apple_sdk");
+ try apple_sdk.addPaths(b, lib);
+ }
+
+ if (b.lazyDependency("zlib", .{})) |upstream| {
+ lib.addIncludePath(upstream.path(""));
+ lib.installHeadersDirectory(
+ upstream.path(""),
+ "",
+ .{ .include_extensions = &.{".h"} },
+ );
+
+ var flags: std.ArrayList([]const u8) = .empty;
+ defer flags.deinit(b.allocator);
+ try flags.appendSlice(b.allocator, &.{
+ "-DHAVE_SYS_TYPES_H",
+ "-DHAVE_STDINT_H",
+ "-DHAVE_STDDEF_H",
+ });
+ if (target.result.os.tag != .windows) {
+ try flags.append(b.allocator, "-DZ_HAVE_UNISTD_H");
+ }
+ if (target.result.abi == .msvc) {
+ try flags.appendSlice(b.allocator, &.{
+ "-D_CRT_SECURE_NO_DEPRECATE",
+ "-D_CRT_NONSTDC_NO_DEPRECATE",
+ });
+ }
+ lib.addCSourceFiles(.{
+ .root = upstream.path(""),
+ .files = srcs,
+ .flags = flags.items,
+ });
+ }
+
+ b.installArtifact(lib);
+}
+
+const srcs: []const []const u8 = &.{
+ "adler32.c",
+ "compress.c",
+ "crc32.c",
+ "deflate.c",
+ "gzclose.c",
+ "gzlib.c",
+ "gzread.c",
+ "gzwrite.c",
+ "inflate.c",
+ "infback.c",
+ "inftrees.c",
+ "inffast.c",
+ "trees.c",
+ "uncompr.c",
+ "zutil.c",
+};
diff --git a/vendor/libghostty-vt/pkg/zlib/build.zig.zon b/vendor/libghostty-vt/pkg/zlib/build.zig.zon
new file mode 100644
index 00000000..0f75bca7
--- /dev/null
+++ b/vendor/libghostty-vt/pkg/zlib/build.zig.zon
@@ -0,0 +1,16 @@
+.{
+ .name = .zlib,
+ .version = "1.3.1",
+ .fingerprint = 0x73887d3aef823f9e,
+ .paths = .{""},
+ .dependencies = .{
+ // madler/zlib
+ .zlib = .{
+ .url = "https://deps.files.ghostty.org/zlib-1220fed0c74e1019b3ee29edae2051788b080cd96e90d56836eea857b0b966742efb.tar.gz",
+ .hash = "N-V-__8AAB0eQwD-0MdOEBmz7intriBReIsIDNlukNVoNu6o",
+ .lazy = true,
+ },
+
+ .apple_sdk = .{ .path = "../apple-sdk" },
+ },
+}